|
|
|
05-20-2020, 08:15 AM
|
#1
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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.
|
|
|
05-20-2020, 09:35 AM
|
#2
|
Human being with feelings
Join Date: May 2017
Location: Leipzig
Posts: 6,621
|
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?
|
|
|
05-20-2020, 01:11 PM
|
#3
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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.
|
|
|
05-20-2020, 01:19 PM
|
#4
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
Quote:
Originally Posted by Meo-Ada Mespotine
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.
|
|
|
05-20-2020, 01:44 PM
|
#5
|
Human being with feelings
Join Date: May 2017
Location: Leipzig
Posts: 6,621
|
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.
|
|
|
05-21-2020, 04:19 AM
|
#6
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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.
|
|
|
05-21-2020, 09:06 AM
|
#7
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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
|
|
|
05-21-2020, 09:33 AM
|
#8
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
Quote:
Originally Posted by Meo-Ada Mespotine
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
|
|
|
05-21-2020, 09:50 AM
|
#9
|
Human being with feelings
Join Date: May 2017
Location: Leipzig
Posts: 6,621
|
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.
|
|
|
05-21-2020, 10:13 AM
|
#10
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
Quote:
Originally Posted by Meo-Ada Mespotine
So first: get all AUXRECV-entries(which is just a couple of lines)
Second: get the second parameter
|
Could you give an example code?
|
|
|
05-21-2020, 10:25 AM
|
#11
|
Human being with feelings
Join Date: Oct 2017
Location: Russia
Posts: 366
|
Quote:
Originally Posted by amagalma
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 )
Last edited by Archie; 05-21-2020 at 10:38 AM.
|
|
|
05-21-2020, 10:32 AM
|
#12
|
Human being with feelings
Join Date: Oct 2017
Location: Russia
Posts: 366
|
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
|
|
|
05-21-2020, 11:34 AM
|
#13
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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.
|
|
|
05-21-2020, 11:40 AM
|
#14
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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
|
|
|
05-21-2020, 11:57 AM
|
#15
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
Large chunk with wanted entry towards the end: gmatch in reverse
Quote:
Originally Posted by Archie
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
|
|
|
05-31-2020, 01:31 AM
|
#16
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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
|
|
|
05-31-2020, 01:38 AM
|
#17
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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.
Last edited by amagalma; 09-03-2020 at 01:23 AM.
|
|
|
06-01-2020, 01:00 AM
|
#18
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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
|
|
|
06-01-2020, 04:32 AM
|
#19
|
Human being with feelings
Join Date: Jun 2012
Location: Spain
Posts: 7,241
|
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.
|
|
|
06-01-2020, 06:58 AM
|
#20
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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.
|
|
|
06-02-2020, 05:47 AM
|
#21
|
Human being with feelings
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 957
|
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:
|
|
|
06-02-2020, 08:12 AM
|
#22
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
Yes. making the math library local boosts its speed up to 30% as you've seen too
|
|
|
06-02-2020, 08:48 AM
|
#23
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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.
Last edited by amagalma; 06-04-2020 at 03:59 AM.
|
|
|
06-04-2020, 04:02 AM
|
#24
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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
|
|
|
06-04-2020, 08:32 AM
|
#25
|
Human being with feelings
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 957
|
Quote:
Originally Posted by amagalma
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.
|
|
|
06-04-2020, 12:36 PM
|
#26
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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...
|
|
|
06-04-2020, 01:04 PM
|
#27
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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
|
|
|
06-05-2020, 11:31 AM
|
#28
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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
|
|
|
06-05-2020, 07:16 PM
|
#29
|
Human being with feelings
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 957
|
Quote:
Originally Posted by amagalma
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
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.
|
|
|
06-07-2020, 03:51 AM
|
#30
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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.
|
|
|
06-08-2020, 04:29 AM
|
#31
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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
|
|
|
06-08-2020, 08:31 AM
|
#32
|
Human being with feelings
Join Date: Dec 2017
Location: Sunny Siberian Islands
Posts: 957
|
Quote:
Originally Posted by amagalma
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):
|
|
|
06-08-2020, 08:43 AM
|
#33
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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!
|
|
|
06-25-2020, 04:28 PM
|
#34
|
Human being with feelings
Join Date: May 2020
Posts: 190
|
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.
|
|
|
06-26-2020, 04:50 AM
|
#35
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
Quote:
Originally Posted by jkooks
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
|
|
|
06-26-2020, 10:29 AM
|
#36
|
Human being with feelings
Join Date: May 2020
Posts: 190
|
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!
|
|
|
07-26-2020, 05:06 PM
|
#37
|
Human being with feelings
Join Date: May 2017
Location: Leipzig
Posts: 6,621
|
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.
|
|
|
07-27-2020, 09:37 AM
|
#38
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
Thanks Mespotine for the tests!
Quote:
Originally Posted by Meo-Ada Mespotine
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.
|
|
|
07-27-2020, 04:32 PM
|
#39
|
Human being with feelings
Join Date: May 2017
Location: Leipzig
Posts: 6,621
|
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.
Last edited by Meo-Ada Mespotine; 07-27-2020 at 04:39 PM.
|
|
|
08-29-2020, 03:34 AM
|
#40
|
Human being with feelings
Join Date: Apr 2011
Posts: 3,451
|
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)
|
|
|
Thread Tools |
|
Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -7. The time now is 04:15 AM.
|