Old 05-20-2020, 08:15 AM   #1
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default Code/function benchmarking

Let's make a thread where we can benchmark functions/code in order to find the fastest/most efficient way of doing things.

Here is a benchmarking function:
Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 1000000 -- Set here the appropriate number
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return reaper.time_precise() - start, function_name( ... )
end
We could name each post depending on what it is about and then post the results of our benchmarks. What do you think?

Last edited by amagalma; 05-20-2020 at 08:20 AM.
amagalma is offline   Reply With Quote
Old 05-20-2020, 09:35 AM   #2
Meo-Ada Mespotine
Human being with feelings
 
Meo-Ada Mespotine's Avatar
 
Join Date: Apr 2020
Location: Leipzig
Posts: 279
Default

https://mespotin.uber.space/Ultrasch...rk_MeasureTime



Would be interesting.

But I think, that it also means measuring, how Reaper handles certain things.
For instance: is setting a statechunk which changes something more ressource-intensive than setting a statechunk which changes nothing? This would affect the measuring heavily.
Same for other functions, who toggle things, mute, solo, etc. Do they check first, whether toggling is necessary or do they toggle whatsoever, even if the already muted track gets muted again?
__________________
Use you/she/her, when contacting me, please. Thanks :) Not mentoring via PMs, sorry.
Ultraschall-API - 1111 ReaScript functions for Reaper - Reaper Internals - Developerdocs4Reaper
Meo-Ada Mespotine is offline   Reply With Quote
Old 05-20-2020, 01:11 PM   #3
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default Working with track chunks: gsub whole chunk VS table method

Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 100000
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return reaper.time_precise() - start, function_name( ... )
end

-- Get chunk
local ok, str = reaper.GetTrackStateChunk( reaper.GetSelectedTrack(0,0),"", true )

-- Create new GUIDs for everything

function Using_gsub()
  return str:gsub("%b{}", function() return reaper.genGuid() end)
end

function By_Parsing()
  local t = {}
  for line in str:gmatch("[^\n]+") do
    if line:find( "[{}]" ) then
      line = line:gsub("%b{}", function() return reaper.genGuid() end)
    end
    t[#t+1] = line
  end
  return table.concat(t, "\n")
end

speed1 = MeasureSpeed(Using_gsub)
speed2 = MeasureSpeed(By_Parsing)

The usual way of working with chunks is to store everything in a table, change whatever and then concatenate the table in order to set it back to the track.


If what we want to change can be achieved with a clever gsub then it is way faster!

Code:
Using_gsub : 1.5826 seconds
By_Parsing: 5.292 seconds

Last edited by amagalma; 05-21-2020 at 09:09 AM.
amagalma is offline   Reply With Quote
Old 05-20-2020, 01:19 PM   #4
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

Quote:
Originally Posted by Meo-Ada Mespotine View Post
https://mespotin.uber.space/Ultrasch...rk_MeasureTime



Would be interesting.

But I think, that it also means measuring, how Reaper handles certain things.
For instance: is setting a statechunk which changes something more ressource-intensive than setting a statechunk which changes nothing? This would affect the measuring heavily.
Same for other functions, who toggle things, mute, solo, etc. Do they check first, whether toggling is necessary or do they toggle whatsoever, even if the already muted track gets muted again?
All kinds of measurements are useful. But when comparing we have to keep everything the same and change just the thing we are benchmarking.

I personally am interested practically how to achieve the result I want with the fastest and less resource hungry code.
amagalma is offline   Reply With Quote
Old 05-20-2020, 01:44 PM   #5
Meo-Ada Mespotine
Human being with feelings
 
Meo-Ada Mespotine's Avatar
 
Join Date: Apr 2020
Location: Leipzig
Posts: 279
Default

Parsing strings in Lua:

When parsing through strings, using string.gmatch:

Code:
-- parse line by line
for found_part_string in string.gmatch(mystring, "(.-)\n") do
   ...
end
is usually much faster than using the match-function:

Code:
-- parse line by line
while mystring:match("(.-)\n")~=nil do
  found_part_string = mystring:match("(.-)\n") -- get the next line into found_part_string
  mystring=mystring:match("\n(.*)")               -- remove found line from mystring, so we can find 
                                                                   -- the next line in the next loop iteration
  ...
end
It is also better to avoid complex matching-patterns but rather match them in several steps:

1) try to find the bigger part(s) who probably contain the stuff you are looking for
2) patternmatch within the found parts the more detailed ones.

This is much faster, often by magnitudes.

@amalgama
Maybe, we should also collect in here some other hints, bits and pieces in improving code-speed. I guess we all have lessons learnt on a code-strategy-way, who can influence speed of scripts significantly.
__________________
Use you/she/her, when contacting me, please. Thanks :) Not mentoring via PMs, sorry.
Ultraschall-API - 1111 ReaScript functions for Reaper - Reaper Internals - Developerdocs4Reaper
Meo-Ada Mespotine is offline   Reply With Quote
Old 05-21-2020, 04:19 AM   #6
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default Reading a file line by line: io.lines VS gmatch all the file in memory

Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 1000
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return reaper.time_precise() - start, function_name( ... )
end
--------------------------------------------------------------------------

local path = [[G:\REAPER RECORDINGS\Test\1.rpp]]

function readFile1()
  local file = io.open(path)
  local t, i = {}, 0
  for line in file:lines() do
    i = i + 1
    t[i] = line
  end
  file:close()
  return t
end

function readFile2()
  local file = io.open(path)
  local content = file:read("*a")
  file:close()

  local t, i = {}, 0
  for line in content:gmatch("[^\n]+") do
    i = i + 1
    t[i] = line
  end
  return t
end

speed1, t1 = MeasureSpeed( readFile1 )
speed2, t2 = MeasureSpeed( readFile2 )
Code:
Using io.lines = 4.843 seconds
Reading all the file in memory and using gmatch: 1.292 seconds
Bear in mind that you need to have as much free memory as the file you want to read if you go that way.

Last edited by amagalma; 05-21-2020 at 09:08 AM.
amagalma is offline   Reply With Quote
Old 05-21-2020, 09:06 AM   #7
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default Creating arrays in lua: using t[#t+1] VS t[index] VS table.insert

Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 100000
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return reaper.time_precise() - start, function_name( ... )
end
--------------------------------------------------------------------------

function Using_tableLength()
  local t = {}
  for i = 1, 1000 do
    t[#t+1] = true
  end
  return t
end

function Using_number()
  local t = {}
  local index = 0
  for i = 1, 1000 do
    index = index + 1
    t[index] = true
  end
  return t
end

function Using_tableInsert()
  local t = {}
  for i = 1, 1000 do
    table.insert(t, true)
  end
  return t
end

speed1, t1 = MeasureSpeed( Using_tableLength )
speed2, t2 = MeasureSpeed( Using_number )
speed3, t3 = MeasureSpeed( Using_tableInsert )

Code:
Using_tableLength = 4.181 sec
Using_number = 1.951 sec
Using_tableInsert = 7.001 sec
amagalma is offline   Reply With Quote
Old 05-21-2020, 09:33 AM   #8
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

Quote:
Originally Posted by Meo-Ada Mespotine View Post
It is also better to avoid complex matching-patterns but rather match them in several steps:

1) try to find the bigger part(s) who probably contain the stuff you are looking for
2) patternmatch within the found parts the more detailed ones.

This is much faster, often by magnitudes.
Actually this is not true. The less times pattern matching functions are used, the faster is the code. If it is possible to get what you want in one go, it is the best. If not, then try to get it in two runs. The more runs you add the slower it is.

Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 500000
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return reaper.time_precise() - start, function_name( ... )
end
--------------------------------------------------------------------------

local String = 
[[<ITEM
POSITION 3
SNAPOFFS 0
LENGTH 8.5
LOOP 0
ALLTAKES 0
FADEIN 1 0 0 1 0 0
FADEOUT 1 0 0 1 0 0
MUTE 0
SEL 0
IGUID {E0B78846-B521-4D94-BF7B-E292D7376967}
IID 1
NAME "untitled MIDI item"
VOLPAN 1 0 1 -1
SOFFS 0 0
PLAYRATE 1 1 0 -1 0 0.0025
CHANMODE 0
GUID {21627D01-C7F7-494A-9E2C-E20004992E8B}
]]

-- Let's say I want to get the NAME inside the quotes( untitled MIDI item )

function step_by_step()
  local name = ""
  for line in String:gmatch("[^\n]+") do
    if line:find("NAME") then
      name = line:match('"(.+)"')
    end
  end
  return name
end

function one_go()
  return String:match('.+NAME "(.+)"')
end

speed1, n1 = MeasureSpeed( step_by_step )
speed2, n2 = MeasureSpeed( one_go )
Code:
step_by_step : 2.495 sec
one_go: 1.410 sec
amagalma is offline   Reply With Quote
Old 05-21-2020, 09:50 AM   #9
Meo-Ada Mespotine
Human being with feelings
 
Meo-Ada Mespotine's Avatar
 
Join Date: Apr 2020
Location: Leipzig
Posts: 279
Default

Maybe I need to clarify it a little:

If you use pattern matching on a 5 MB string with a complex matching pattern, it takes an awful lot of time.

If you use a more simple matching pattern to get rid of the majority of the 5MB, the resulting string is much faster to patternmatch through in more detail.

For instance:
If you have a TrackStateChunk with Megabytes of VST-information or millions of mediaitems stored, but all you want to know is the second routing-parameter information, passing the string for AUXRECV-entries only is much faster, than for the AUXRECV AND the second parameter at the same time.

So first: get all AUXRECV-entries(which is just a couple of lines)
Second: get the second parameter

This is significantly faster. That's a lesson I learnt from writing my GetProjectLength-function, which parses the project-length from a projectfile.

In combination with clever use of string.gsub and string.gmatch, you can improve the parsing speed significantly.

StateChunk:match("AUXREC .- (.-) .-\n") is slower, when applying it to a multimegabyte string. So minimising the size of string to parse through with more detailed patternmatching is actually faster, depending on how much you want from a string.

The more complex the matching pattern, the slower it gets in huge strings. With short strings, this shouldn't be a problem.

This has probably to do with the amount of Ram used for the string, as I found a similar behavior with huge tables. Splitting them into two parts can improve the speed of code, even though in combination, it still needs the same amount of Ram. But it's just guessing on my side.
__________________
Use you/she/her, when contacting me, please. Thanks :) Not mentoring via PMs, sorry.
Ultraschall-API - 1111 ReaScript functions for Reaper - Reaper Internals - Developerdocs4Reaper
Meo-Ada Mespotine is offline   Reply With Quote
Old 05-21-2020, 10:13 AM   #10
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

Quote:
Originally Posted by Meo-Ada Mespotine View Post
So first: get all AUXRECV-entries(which is just a couple of lines)
Second: get the second parameter

Could you give an example code?
amagalma is offline   Reply With Quote
Old 05-21-2020, 10:25 AM   #11
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

Quote:
Originally Posted by amagalma View Post
Actually this is not true. The less times pattern matching functions are used, the faster is the code. If it is possible to get what you want in one go, it is the best. If not, then try to get it in two runs. The more runs you add the slower it is.

Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 500000
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return reaper.time_precise() - start, function_name( ... )
end
--------------------------------------------------------------------------

local String = 
[[<ITEM
POSITION 3
SNAPOFFS 0
LENGTH 8.5
LOOP 0
ALLTAKES 0
FADEIN 1 0 0 1 0 0
FADEOUT 1 0 0 1 0 0
MUTE 0
SEL 0
IGUID {E0B78846-B521-4D94-BF7B-E292D7376967}
IID 1
NAME "untitled MIDI item"
VOLPAN 1 0 1 -1
SOFFS 0 0
PLAYRATE 1 1 0 -1 0 0.0025
CHANMODE 0
GUID {21627D01-C7F7-494A-9E2C-E20004992E8B}
]]

-- Let's say I want to get the NAME inside the quotes( untitled MIDI item )

function step_by_step()
  local name = ""
  for line in String:gmatch("[^\n]+") do
    if line:find("NAME") then
      name = line:match('"(.+)"')
    end
  end
  return name
end

function one_go()
  return String:match('.+NAME "(.+)"')
end

speed1, n1 = MeasureSpeed( step_by_step )
speed2, n2 = MeasureSpeed( one_go )
Code:
step_by_step : 2.495 sec
one_go: 1.410 sec
A little wrong test, 'return' not there.
"match" searches for the first occurrence, and in the function "step_by_step" you iterate over the entire file. And you got a difference in half, although it is much smaller. Yes - But anyway, one call is faster anyway.

Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 500000
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return reaper.time_precise() - start, function_name( ... )
end
--------------------------------------------------------------------------

local String = 
[[<ITEM
POSITION 3
SNAPOFFS 0
LENGTH 8.5
LOOP 0
ALLTAKES 0
FADEIN 1 0 0 1 0 0
FADEOUT 1 0 0 1 0 0
MUTE 0
SEL 0
IGUID {E0B78846-B521-4D94-BF7B-E292D7376967}
IID 1
NAME "untitled MIDI item"
VOLPAN 1 0 1 -1
SOFFS 0 0
PLAYRATE 1 1 0 -1 0 0.0025
CHANMODE 0
GUID {21627D01-C7F7-494A-9E2C-E20004992E8B}
]]

-- Let's say I want to get the NAME inside the quotes( untitled MIDI item )

function step_by_step()
  local name = ""
  for line in String:gmatch("[^\n]+") do
    if line:find("NAME") then
      name = line:match('"(.+)"')
      return name --  <<<<<<<
    end
  end
end

function one_go()
  return String:match('.+NAME "(.+)"')
end

speed1, n1 = MeasureSpeed( step_by_step )
speed2, n2 = MeasureSpeed( one_go )
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |

Last edited by Archie; 05-21-2020 at 10:38 AM.
Archie is offline   Reply With Quote
Old 05-21-2020, 10:32 AM   #12
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

If a large file, then you need to choose from the situation.
If the desired material is at the beginning, then "string.gmatch" will undoubtedly win, and if at the end, then ":match("bla bla .- (.-) .-\n")"
And the difference is huge.

test1
Code:
   step_by_step = 0.084
   one_go = 16.167
test2
Code:
    step_by_step = 43.670
    one_go = 16.341
Attached Files
File Type: rar Test.rar (1.8 KB, 3 views)
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |
Archie is offline   Reply With Quote
Old 05-21-2020, 11:34 AM   #13
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

Both observations are correct!

It depends on the situation and where you expect what interests you to be (towards the beginning or the end). An optimal function would be the one that chooses the correct method automatically.
amagalma is offline   Reply With Quote
Old 05-21-2020, 11:40 AM   #14
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

In mespotine's example, actually both methods give the same results:
Code:
local _, chunk = reaper.GetTrackStateChunk( reaper.GetTrack(0,0), "", false )
-- my example track chunk has 8719 lines
-- times_to_run = 1000


function step_by_step()
  -- So first: get all AUXRECV-entries(which is just a couple of lines)
  local t = {}
  for entry in chunk:gmatch("AUXRECV.-\n") do
    -- Second: get the second parameter
    -- AUXRECV 1 (0) 1 0 0 0 0 0 0 -1:U 0 -1 ''
    local number = entry:match("AUXRECV %d (%d)")
    if number then t[#t+1] = number end
  end
  return t
end

function one_go()
  local t = {}
  for number in chunk:gmatch("AUXRECV %d (%d)") do
    t[#t+1] = number
  end
  return t
end
Code:
step_by_step: 4.129 sec
one_go: 4.136 sec
amagalma is offline   Reply With Quote
Old 05-21-2020, 11:57 AM   #15
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default Large chunk with wanted entry towards the end: gmatch in reverse

Quote:
Originally Posted by Archie View Post
If a large file, then you need to choose from the situation.
If the desired material is at the beginning, then "string.gmatch" will undoubtedly win, and if at the end, then ":match("bla bla .- (.-) .-\n")"
And the difference is huge.

test1
Code:
   step_by_step = 0.084
   one_go = 16.167
test2
Code:
    step_by_step = 43.670
    one_go = 16.341

There is a third option: to go step by step but in reverse!
Code:
-- using the string in your test2.file


 function step_by_step()
  local name = ""
  for line in String:gmatch("[^\n]+") do
    if line:find("NAME") then
      name = line:match('"(.+)"')
      return name
    end
  end
end

function one_go()
  return String:match('.+NAME "(.+)"')
end


function reverse_step_by_step()
  local name = ""
  local String_rev = String:reverse()
  for line in String_rev:gmatch("[^\n]+") do
    if line:find("EMAN") then
      name = line:match('"(.+)"')
      return name:reverse()
    end
  end
end


speed1, n1 = MeasureSpeed( step_by_step )
speed2, n2 = MeasureSpeed( one_go )
speed3, n3 = MeasureSpeed( reverse_step_by_step )
Code:
step_by_step: 5.216 sec
one_go: 1.670 sec
reverse_step_by_step: 1.613 sec
amagalma is offline   Reply With Quote
Old 05-31-2020, 01:31 AM   #16
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default Get parent track of item - fastest API

Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 20000000
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return reaper.time_precise() - start, function_name( ... )
end

local item = reaper.GetSelectedMediaItem(0,0)

speed1 = MeasureSpeed( reaper.GetMediaItemInfo_Value, item, "P_TRACK" )
speed2 = MeasureSpeed( reaper.GetMediaItem_Track, item )
speed3 = MeasureSpeed( reaper.GetMediaItemTrack, item )
Code:
speed1 = 1.028 seconds
speed2 = 0.608
speed3 = 0.618
amagalma is offline   Reply With Quote
Old 05-31-2020, 01:38 AM   #17
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default GetTrackNumMediaItems VS CountTrackMediaItems

Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 80000000
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return reaper.time_precise() - start, function_name( ... )
end

local track = reaper.GetTrack(0,0)

speed1 = MeasureSpeed( reaper.GetTrackNumMediaItems, track )
speed2 = MeasureSpeed( reaper.CountTrackMediaItems, track )
Code:
GetTrackNumMediaItems =  2.79 seconds
CountTrackMediaItems =  2.90
amagalma is offline   Reply With Quote
Old 06-01-2020, 01:00 AM   #18
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default Escaping lua magic characters

Code:
// times_to_run = 500000

local function esc(s)
  local matches =
  {
    ["^"] = "%^",
    ["$"] = "%$",
    ["("] = "%(",
    [")"] = "%)",
    ["%"] = "%%",
    ["."] = "%.",
    ["["] = "%[",
    ["]"] = "%]",
    ["*"] = "%*",
    ["+"] = "%+",
    ["-"] = "%-",
    ["?"] = "%?",
  }
  return (s:gsub(".", matches))
end

local function literalize(str)
  return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", function(c) return "%" .. c end)
end

local function literalize2(str)
  return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")
end

str = "This is a test. 2 + 2 = 4. 4$ - 4$ = ? ( equals 0$ ) * But I am not 100% sure [^^]"

speed1, res1 = MeasureSpeed( esc, str )
speed2, res2 = MeasureSpeed( literalize, str )
speed3, res3 = MeasureSpeed( literalize2, str )
Code:
esc = 2.893 sec
literalize = 7.578 sec
literalize2 = 6.966 sec
amagalma is offline   Reply With Quote
Old 06-01-2020, 04:32 AM   #19
heda
Human being with feelings
 
heda's Avatar
 
Join Date: Jun 2012
Location: Spain
Posts: 5,826
Default

oh...
that function esc(s) is quite an improvement! I used literalize on entire big chunks so this may improve things.. I'll test it
Thank you @amagalma !

Last edited by heda; 06-01-2020 at 04:51 AM.
heda is offline   Reply With Quote
Old 06-01-2020, 06:58 AM   #20
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

If you are working with track chunks, depending on what you do exactly with them, there may be some more room of optimization. Have a look at the first posts in this thread.
amagalma is offline   Reply With Quote
Old Yesterday, 05:47 AM   #21
cool
Human being with feelings
 
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 279
Default

Good topic, thanks for the ideas!

I decided to test the classic recommendations for increasing the performance of LUA. A simple reduction and declaration of functions at the beginning of the code gives an increase in speed of about 30%.
But besides "r = reaper". In simple tests, this did not give a gain. Perhaps it will be noticeable with large code sizes. While I do not know how it can be tested correctly.

Code:
local r = reaper;
local abs  = math.abs
local min  = math.min
local max  = math.max
local sqrt = math.sqrt
local ceil  = math.ceil
local floor = math.floor   
local exp = math.exp
local log = math.log
local huge = math.huge  

local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 500000
  local start = r.time_precise()
  for i = 1, times_to_run do
    function_name( ... )
  end
  -- returns time elapsed and function's results
  return r.time_precise() - start, function_name( ... )
end

function short_abs()
  return abs(1000000)
end

function short_min()
  return min(1000000)
end

function short_max()
  return max(1000000)
end

function short_sqrt()
  return sqrt(1000000)
end

function short_ceil()
  return ceil(1000000)
end

function short_floor()
  return floor(1000000)
end

function short_exp()
  return exp(1000000)
end

function short_log()
  return log(1000000)
end

function short_huge()
  return huge, huge
end
-----------------------------------------------------
function full_abs()
  return math.abs(1000000)
end

function full_min()
  return math.min(1000000)
end

function full_max()
  return math.max(1000000)
end

function full_sqrt()
  return math.sqrt(1000000)
end

function full_ceil()
  return math.ceil(1000000)
end

function full_floor()
  return math.floor(1000000)
end

function full_exp()
  return math.exp(1000000)
end

function full_log()
  return math.log(1000000)
end

function full_huge()
  return math.huge, huge
end

speed1short, n1 = MeasureSpeed( short_abs )
speed1__full, n2 = MeasureSpeed( full_abs )
speed2short, n3 = MeasureSpeed( short_min )
speed2__full, n4 = MeasureSpeed( full_min )
speed3short, n5 = MeasureSpeed( short_max )
speed3__full, n6 = MeasureSpeed( full_max )
speed4short, n7 = MeasureSpeed( short_sqrt )
speed4__full, n8 = MeasureSpeed( full_sqrt )
speed5short, n9 = MeasureSpeed( short_ceil )
speed5__full, n10 = MeasureSpeed( full_ceil )
speed6short, n11 = MeasureSpeed( short_floor )
speed6__full, n12 = MeasureSpeed( full_floor )
speed7short, n13 = MeasureSpeed( short_exp )
speed7__full, n14 = MeasureSpeed( full_exp )
speed8short, n15 = MeasureSpeed( short_log )
speed8__full, n16 = MeasureSpeed( full_log )
speed9short, n17 = MeasureSpeed( short_huge )
speed9__full, n18 = MeasureSpeed( full_huge )


Results:
cool is offline   Reply With Quote
Old Yesterday, 08:12 AM   #22
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

Yes. making the math library local boosts its speed up to 30% as you've seen too
amagalma is offline   Reply With Quote
Old Yesterday, 08:48 AM   #23
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default local reaper = reaper // seems to be a myth

Code:
times_to_run = 10

-- project with 100 empty tracks
Code:
function TrackChunk()
  local cnt = reaper.GetNumTracks()
  for i = 0, cnt-1 do
    local track = reaper.GetTrack(0,i)
    local _, chunk = reaper.GetTrackStateChunk( track, "", true )
    reaper.SetTrackStateChunk( track, chunk, true )
    local _, name = reaper.GetTrackName( track )
  end
end


local GetNumTracks, GetTrack = reaper.GetNumTracks, reaper.GetTrack
local GetTrackStateChunk, GetTrackName = reaper.GetTrackStateChunk, reaper.GetTrackName
local SetTrackStateChunk = reaper.SetTrackStateChunk
function AsLocalFunctions()
  local cnt = GetNumTracks()
  for i = 0, cnt-1 do
    local track = GetTrack(0,i)
    local _, chunk = GetTrackStateChunk( track, "", true )
    SetTrackStateChunk( track, chunk, true )
    local _, name = GetTrackName( track )
  end
end

------------------------------------------------------

-- test as it is out-of-the-box
speed1, res1 = MeasureSpeed(TrackChunk)

-- make the whole reaper library local
local reaper = reaper
speed2, res2 = MeasureSpeed(TrackChunk)

-- store needed functions into local variables
speed3, res3 = MeasureSpeed(AsLocalFunctions)
Code:
as it is = 20.414
local reaper = 20.392
local variables = 19.542
I run it several times and I got similar results.

Remarks:
- Making the reaper library local does not offer any speed boost..
- Moving the reaper library into the global section does not change anything in speed (for func in pairs(reaper) do _G[func]=reaper[func] end)
- Storing the needed functions into local variables offers a slight boost of 1-4%

Last edited by amagalma; Yesterday at 09:24 AM.
amagalma is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 06:31 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.