Old 05-20-2020, 08:15 AM   #1
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
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?
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)

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: May 2017
Location: Leipzig
Posts: 6,621
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.Ultraschall-Api Lua Api4Reaper - Donate, if you wish

On vacation for the time being...
Meo-Ada Mespotine is online now   Reply With Quote
Old 05-20-2020, 01:11 PM   #3
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
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
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)

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: 3,451
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.
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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: May 2017
Location: Leipzig
Posts: 6,621
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.Ultraschall-Api Lua Api4Reaper - Donate, if you wish

On vacation for the time being...
Meo-Ada Mespotine is online now   Reply With Quote
Old 05-21-2020, 04:19 AM   #6
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
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.
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)

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: 3,451
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
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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: 3,451
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
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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: May 2017
Location: Leipzig
Posts: 6,621
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.Ultraschall-Api Lua Api4Reaper - Donate, if you wish

On vacation for the time being...
Meo-Ada Mespotine is online now   Reply With Quote
Old 05-21-2020, 10:13 AM   #10
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
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?
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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: 366
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 | Donate2 | Donate3 | PayPal |

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: 366
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, 158 views)
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate | Donate2 | Donate3 | PayPal |
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: 3,451
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.
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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: 3,451
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
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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: 3,451
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
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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: 3,451
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
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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: 3,451
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

Edit.. If not repeating the test thousands of times, then CountTrackMediaItems is actually faster.
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)

Last edited by amagalma; 09-03-2020 at 01:23 AM.
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: 3,451
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
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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: 7,241
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: 3,451
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.
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-02-2020, 05:47 AM   #21
cool
Human being with feelings
 
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 957
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 06-02-2020, 08:12 AM   #22
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default

Yes. making the math library local boosts its speed up to 30% as you've seen too
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-02-2020, 08:48 AM   #23
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default local reaper = reaper // does not offer any speed boost

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% After multiple measurements it seems that storing the needed functions into local variables does not offer any speed boost.

There is not anything one can do to speed up the reaper library in lua. It is already optimized.
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)

Last edited by amagalma; 06-04-2020 at 03:59 AM.
amagalma is offline   Reply With Quote
Old 06-04-2020, 04:02 AM   #24
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default track chunks: eel VS lua

Comparison of code that extracts all the items' chunk from all tracks.

Lua code:
Code:
local function GetTrackItemsChunk( track ) -- returns items_chunk
  if reaper.CountTrackMediaItems( track ) < 1 then return end
  local _, chunk = reaper.GetTrackStateChunk( track, "", false )
  return chunk:match("(<ITEM.+>\n)>\n?$")
end

local count = reaper.GetNumTracks(0)

local start = reaper.time_precise()

for i = 0, count-1 do
  local track = reaper.GetTrack(0,i)
  local itemsChunk = GetTrackItemsChunk( track )
end

speed = reaper.time_precise() - start
EEL code:
Code:
function GetTrackItemsChunk( track, itemsChunk ) (
  GetTrackStateChunk( track, chunk = #, 0 );
  match("*?<ITEM%s+>\n", chunk, temp);
  sprintf(itemsChunk,"%s%s", "<ITEM", temp)
);

count = GetNumTracks(0);

start = time_precise();

i = 0;
loop(count,
  track = GetTrack(0,i);
  GetTrackItemsChunk( track, #itemsChunk );
  i += 1;
);

speed = time_precise() - start;
Code:
EEL: 1.816 (about 15% faster)
Lua: 2.129
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-04-2020, 08:32 AM   #25
cool
Human being with feelings
 
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 957
Default

Quote:
Originally Posted by amagalma View Post
Code:
as it is = 20.414
local reaper = 20.392
local variables = 19.542
I run it several times and I got similar results.

There is not anything one can do to speed up the reaper library in lua. It is already optimized.

Thanks for the test and testing code! The results are interesting, and the most interesting is that I got completely different results
But, my code looks a little different. I hope I have not made a mistake
Also, I got different results with a different number of iterations.

Code:
local r = reaper

local GetNumTracks, GetTrack = reaper.GetNumTracks, reaper.GetTrack
local GetTrackStateChunk, GetTrackName = reaper.GetTrackStateChunk, reaper.GetTrackName
local SetTrackStateChunk = reaper.SetTrackStateChunk

local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 10
  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 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

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

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
speed2, res2 = MeasureSpeed(TrackChunk_short)

-- store needed functions into local variables
speed3, res3 = MeasureSpeed(AsLocalFunctions)

Code:
100 tracks
times_to_run = 10
as it is = 13.1
local reaper = 10.1
local variables = 10.1
Code:
100 tracks
times_to_run = 1
as it is = 2.0
local reaper = 1.5
local variables = 1.0
If I have not made a mistake and the testing conditions are correct, then this is an impressive discovery - a 30 - 100% increase in code performance by simple means is a miracle

And yet, I noticed an interesting feature of the ReaScript Development Environment: if you run the script several times over and over, then sometimes the values ​​of speed2 and speed3 gradually increase and "catch up" speed1. But it’s enough to make any change to the file (for example, put a space and save the changes), the script may work fine again for a while. This is strange.
cool is offline   Reply With Quote
Old 06-04-2020, 12:36 PM   #26
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default

I just run your test version and I've got the same results as you!
Code:
Times to run: 10
as it is = 16.037
local reaper = 13.348
local variables = 13.400
Code:
When run only once:
as it is = 1.994
local reaper = 1.997
local variables = 1.997
This is puzzling...
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-04-2020, 01:04 PM   #27
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default

Test with 4 different scripts:
Test on an empty project with 200 empty tracks

Code:
local start = reaper.time_precise()
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
local speed = reaper.time_precise() - start
reaper.ShowConsoleMsg(string.format("Out of the box : %.3f seconds\n\n", speed))
Code:
local reaper = reaper
local start = reaper.time_precise()
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
local speed = reaper.time_precise() - start
reaper.ShowConsoleMsg(string.format("local reaper = reaper : %.3f seconds\n\n", speed))
Code:
local r = reaper
local start = r.time_precise()
local cnt = r.GetNumTracks()
for i = 0, cnt-1 do
    local track = r.GetTrack(0,i)
    local _, chunk = r.GetTrackStateChunk( track, "", true )
    r.SetTrackStateChunk( track, chunk, true )
    local _, name = r.GetTrackName( track )
end
local speed = r.time_precise() - start
r.ShowConsoleMsg(string.format("local r = reaper : %.3f seconds\n\n", speed))
Code:
local GetNumTracks, GetTrack = reaper.GetNumTracks, reaper.GetTrack
local GetTrackStateChunk, GetTrackName = reaper.GetTrackStateChunk, reaper.GetTrackName
local SetTrackStateChunk = reaper.SetTrackStateChunk

local start = reaper.time_precise()
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
local speed = reaper.time_precise() - start
reaper.ShowConsoleMsg(string.format("local functions : %.3f seconds\n\n", speed))
The results confirm my findings:
Code:
Out of the box : 8.731 seconds
local reaper = reaper : 8.691 seconds
local r = reaper : 8.929 seconds
local functions : 8.732 seconds
No benefits. Reaper library is already optimized
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-05-2020, 11:31 AM   #28
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default Lua vs EEL: arrays (huuuuuge difference in speed)

Create an array and get the value of its penultimate item.

Lua:
Code:
local start = reaper.time_precise()
local array = {}
for a = 1, 1000002 do
  array[a] = a + 10
end
Z = array[1000001]
speed = reaper.time_precise() - start
EEL:
Code:
time_precise(start);
array = 0;
a = 0;
loop(1000001,
  array[a] = a + 10;
  a+=1;
);
Z = array[1000000];
speed = time_precise() - start;
Code:
Lua: 0.02739 (668% slower than EEL)
EEL: 0.00410
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-05-2020, 07:16 PM   #29
cool
Human being with feelings
 
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 957
Default

Quote:
Originally Posted by amagalma View Post
The results confirm my findings:
Code:
Out of the box : 8.731 seconds
local reaper = reaper : 8.691 seconds
local r = reaper : 8.929 seconds
local functions : 8.732 seconds
No benefits. Reaper library is already optimized

The myth is busted Thank you for delving into this!

It seems that in my code the performance increase was due to another:
I tried my script with three identical code parts and got the same result with increased performance.
It seems like this: if in a script the code with the same commands is repeated several times, then, starting from the second execution, all subsequent repeated operations will receive a boost of 30% or higher.

Code:
local function MeasureSpeed( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 10
  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 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

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

local function MeasureSpeed2( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 10
  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 TrackChunk_short()
  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

-- make the whole reaper library local
speed2, res2 = MeasureSpeed2(TrackChunk_short)

local function MeasureSpeed3( function_name, ... )
  -- enter function name and arguments, all comma-separated
  local times_to_run = 10
  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 AsLocalFunctions()
  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

-- store needed functions into local variables
speed3, res3 = MeasureSpeed3(AsLocalFunctions)

Also, I tested the same code, but without comments, without spaces and without hyphens (kawa style):

Code:
local function MeasureSpeed( function_name, ... ) local times_to_run = 10 local start = reaper.time_precise() for i = 1, times_to_run do function_name( ... ) end return reaper.time_precise() - start, function_name( ... ) end 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 speed1, res1 = MeasureSpeed(TrackChunk) local function MeasureSpeed2( function_name, ... ) local times_to_run = 10 local start = reaper.time_precise() for i = 1, times_to_run do function_name( ... ) end return reaper.time_precise() - start, function_name( ... ) end function TrackChunk_short() 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 speed2, res2 = MeasureSpeed2(TrackChunk_short) local function MeasureSpeed3( function_name, ... ) local times_to_run = 10 local start = reaper.time_precise() for i = 1, times_to_run do function_name( ... ) end return reaper.time_precise() - start, function_name( ... ) end function AsLocalFunctions() 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 speed3, res3 = MeasureSpeed3(AsLocalFunctions)
It works just like regular code, the difference is within the margin of error.

Quote:
Originally Posted by amagalma View Post
Code:
Lua: 0.02739 (668% slower than EEL)
EEL: 0.00410
Wow! EEL rocks

Last edited by cool; 06-05-2020 at 07:24 PM.
cool is offline   Reply With Quote
Old 06-07-2020, 03:51 AM   #30
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default

Hmm, I don't see the point of the code in your post... Running the code gives me three same speed results, as expected...


.. Yes, EEL is a lot faster when working with arrays (almost 7 times) and when using the native chunk functions (about 15%). In the rest of the cases Lua is as fast, plus simpler, plus has tables which are very powerful.
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-08-2020, 04:29 AM   #31
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default Getting the integral part of a number: math.modf VS math.floor

Code:
-- times_to_run = 1
local array = {}
local random = math.random
math.randomseed(reaper.time_precise())
random(); random(); random()

for i=1, 10000000 do
  array[i] = random()
end

speed_floor, speed_modf = 0, 0

for i = 1, #array do
  local speedf = MeasureSpeed(math.floor, array[i])
  local speedm = MeasureSpeed(math.modf, array[i])
  speed_floor = speed_floor + speedf
  speed_modf = speed_modf + speedm
end
Code:
speed_floor = 0.941
speed_modf = 0.950
The difference in speed is really small, but in all tests the floor is always slightly faster than modf. So, unless one wants the fractional part too, it is a bit faster to use math.floor
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-08-2020, 08:31 AM   #32
cool
Human being with feelings
 
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 957
Default

Quote:
Originally Posted by amagalma View Post
Hmm, I don't see the point of the code in your post... Running the code gives me three same speed results, as expected...

It is strange that we get different results. I do not know why this happens.

I tested three times under the same conditions (one hundred tracks):


cool is offline   Reply With Quote
Old 06-08-2020, 08:43 AM   #33
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default

I checked with 30 tracks, sorry!
I've just tested with 100 tracks as you did:
Code:
speedl 20.8519861
speed2 20.8542173
speed3 20.8537364
As expected... I have no idea why you get such strange results though! :S

BTW, your system is twice as fast as mine!
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-25-2020, 04:28 PM   #34
jkooks
Human being with feelings
 
Join Date: May 2020
Posts: 190
Default

Not sure if anyone has had this problem before or not, but does anyone know of a time efficient manner to enumerate all of the actions a user has? I'm trying to run through all of them and only pick out the scripts, but it is taking around 18 or so seconds to do so and I am curious if there is a way to shave that time down.

As of now, the way I am checking if it is a script is by seeing if the first character of the name is "S" and if it is checking to see if it starts with "Script: " as an added safety net.
jkooks is offline   Reply With Quote
Old 06-26-2020, 04:50 AM   #35
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default

Quote:
Originally Posted by jkooks View Post
Not sure if anyone has had this problem before or not, but does anyone know of a time efficient manner to enumerate all of the actions a user has? I'm trying to run through all of them and only pick out the scripts, but it is taking around 18 or so seconds to do so and I am curious if there is a way to shave that time down.

As of now, the way I am checking if it is a script is by seeing if the first character of the name is "S" and if it is checking to see if it starts with "Script: " as an added safety net.

You are doing something wrong...
Code:
local i = 0
local count_scripts = 0
local total_actions = 0
repeat
  local cmd, name = reaper.CF_EnumerateActions( 0, i, "" )
  total_actions = total_actions + 1
  if name:find("^Script:") then
    count_scripts = count_scripts + 1
  end
  i = i + 1
until cmd == 0
reaper.ShowConsoleMsg("Counted " .. total_actions-1 .. " actions in Main.\n" ..
count_scripts .. " of them are scripts.\n")

Code:
Counted 10383 actions in Main.
4104 of them are scripts.

Takes 7ms in my system..



Btw, this is not related to what this thread is about
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 06-26-2020, 10:29 AM   #36
jkooks
Human being with feelings
 
Join Date: May 2020
Posts: 190
Default

Haha, I guess so! I'll take a look at my code and figure out what is causing it to take so long. And alright, sorry about that!
jkooks is offline   Reply With Quote
Old 07-26-2020, 05:06 PM   #37
Meo-Ada Mespotine
Human being with feelings
 
Meo-Ada Mespotine's Avatar
 
Join Date: May 2017
Location: Leipzig
Posts: 6,621
Default

In regards of extstates/gmem, I found that

Reading

GMem - reaper.gmem_read()
is the fastest way to read. So if numbers are the information you want to transmit(without being persistant), they are the way to go.

On my system. reading out 1 000 000 of them takes around 0.115 seconds.

ProjExtStates - reaper.GetProjExtState()
are much slower but a little faster than regular extstates. So consider using them for strings, if speed is an issue but not, where to store things.

On my system. reading out 1 000 000 of them takes around 0.4 seconds.

Regular Extstates - GetExtState
they are the slowest. There's no difference between persistant and non-persistant extstates. Use them, when speed is not an issue.

On my system. reading out 1 000 000 of them takes around 0.48 seconds.


Writing
GMem - reaper.gmem_write()
With writing, it seems to be the same: gmem is fastest.

On my system. reading out 1 000 000 of them takes around 0.13 seconds.

ProjExtStates - reaper.SetProjExtState()
are much slower but a little faster than regular extstates. So consider using them for strings, if speed is an issue but not, where to store things.

On my system. reading out 1 000 000 of them takes around 0.63 seconds.

Regular Extstates - SetExtState
they are the slowest. There's a HUGE difference between persistant and non-persistant extstates. Use them, when speed is not an issue.

Non Persistant-Extstates: On my system. reading out 1 000 000 of them takes around 0.67 seconds.
Persistant-Extstates: On my system. reading out 1 000 000 of them takes around 487.5 seconds.
__________________
Use you/she/her.Ultraschall-Api Lua Api4Reaper - Donate, if you wish

On vacation for the time being...
Meo-Ada Mespotine is online now   Reply With Quote
Old 07-27-2020, 09:37 AM   #38
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default

Thanks Mespotine for the tests!

Quote:
Originally Posted by Meo-Ada Mespotine View Post
Non Persistant-Extstates: On my system. reading out 1 000 000 of them takes around 0.67 seconds.
Persistant-Extstates: On my system. reading out 1 000 000 of them takes around 487.5 seconds.
!!! When using persistent-exstates you read/write from/to the reaper-extstate.ini (to disk) and it is expected to be slower than in all other cases where you read/write directly from/to the memory. But such a huge difference!? Are you sure something else wasn't happening to the disk while testing? (defragmentation, antivirus check, windows update). It is an ENORMOUS difference in speed.. Of course, writting 1 million persistent extstates is not a real-life scenario, and one should use them if he/she needs them.

Hmm.. an interesting test would be comparing the speed of writing/reading to/from the extstate.ini vs writing/reading to/from a dedicated script file.
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
amagalma is offline   Reply With Quote
Old 07-27-2020, 04:32 PM   #39
Meo-Ada Mespotine
Human being with feelings
 
Meo-Ada Mespotine's Avatar
 
Join Date: May 2017
Location: Leipzig
Posts: 6,621
Default

Yes, only that. Keep in mind, I wrote to it a million times on an old computer with old hard drives. And it's not junk st writing but also reading and string replacement happening one million times.
Reading persistent ones isn't a problem as they are kept in memory and read only from extstate.ini when Reaper starts up,afair.
__________________
Use you/she/her.Ultraschall-Api Lua Api4Reaper - Donate, if you wish

On vacation for the time being...

Last edited by Meo-Ada Mespotine; 07-27-2020 at 04:39 PM.
Meo-Ada Mespotine is online now   Reply With Quote
Old 08-29-2020, 03:34 AM   #40
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 3,451
Default Lua tables: hash vs array (string vs number)

Code:
-- Measure how fast is to store strings or numbers in a table

reaper.ClearConsole()

local start = reaper.time_precise()
local strings = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", "272", "273", "274", "275", "276", "277", "278", "279", "280", "281", "282", "283", "284", "285", "286", "287", "288", "289", "290", "291", "292", "293", "294", "295", "296", "297", "298", "299", "300", "301", "302", "303", "304", "305", "306", "307", "308", "309", "310", "311", "312", "313", "314", "315", "316", "317", "318", "319", "320", "321", "322", "323", "324", "325", "326", "327", "328", "329", "330", "331", "332", "333", "334", "335", "336", "337", "338", "339", "340", "341", "342", "343", "344", "345", "346", "347", "348", "349", "350", "351", "352", "353", "354", "355", "356", "357", "358", "359", "360", "361", "362", "363", "364", "365", "366", "367", "368", "369", "370", "371", "372", "373", "374", "375", "376", "377", "378", "379", "380", "381", "382", "383", "384", "385", "386", "387", "388", "389", "390", "391", "392", "393", "394", "395", "396", "397", "398", "399", "400", "401", "402", "403", "404", "405", "406", "407", "408", "409", "410", "411", "412", "413", "414", "415", "416", "417", "418", "419", "420", "421", "422", "423", "424", "425", "426", "427", "428", "429", "430", "431", "432", "433", "434", "435", "436", "437", "438", "439", "440", "441", "442", "443", "444", "445", "446", "447", "448", "449", "450", "451", "452", "453", "454", "455", "456", "457", "458", "459", "460", "461", "462", "463", "464", "465", "466", "467", "468", "469", "470", "471", "472", "473", "474", "475", "476", "477", "478", "479", "480", "481", "482", "483", "484", "485", "486", "487", "488", "489", "490", "491", "492", "493", "494", "495", "496", "497", "498", "499", "500" }
local strings_speed = reaper.time_precise() - start

start = reaper.time_precise()
local numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500 }
local numbers_speed = reaper.time_precise() - start

local faster = strings_speed < numbers_speed and "strings" or "numbers"
local perc = faster == "strings" and strings_speed/numbers_speed or numbers_speed/strings_speed
perc = string.format( "%.1f", ((1 - perc) * 100))
reaper.ShowConsoleMsg("It is " .. perc .. "% faster to populate a table with " .. faster .. "\n\n")


-- Measure how fast is to index a table by numbers (array) or strings (hash table)

local t = {}
start = reaper.time_precise()
for i = 1, 500 do
  t[strings[i]] = true
end
strings_speed = reaper.time_precise() - start

t = {}
start = reaper.time_precise()
for i = 1, 500 do
  t[numbers[i]] = true
end
numbers_speed = reaper.time_precise() - start

faster = strings_speed < numbers_speed and " hash" or "n array"
perc = faster == " hash" and strings_speed/numbers_speed or numbers_speed/strings_speed
perc = string.format( "%.1f", ((1 - perc) * 100))

reaper.ShowConsoleMsg("It is " .. perc .. "% faster to create a" .. faster .. " table\n\n\n")

Code:
It is 11% to 39% faster to store numbers in a table than to store strings.

It is 60% to 67% faster to create an array table (indexed by numbers) than a hash table (indexed by strings).

So, if you want to create a big and fast table, and are willing to sacrifice the convenience that hash tables give when programming, and your program could really work without hash tables, then you should stick to tables indexed by numbers. They are about 63% faster (which means they take about 1/3 of the time to be created)
__________________
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)
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 04:15 AM.


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