@ffx @joule This example can be optimised even further…
My example function “findDeviceInTracksC” runs approx 3-4x faster than A and B.
It’s really all about knowing the weird quirks, I guess.
You just have to experiment a bit and see what works best. (Without driving yourself completely insane over CPU cycles…)
Either way… “Ugly” code or not… If it’s a critical function within your tool (ooeerr!) then its performance should be your main concern, imho, and it’s definitely worth the effort to dig deep…
Cheers,
K.
-- Global song reference.
-- (As per the original post...)
local g_song = renoise.song()
-- Method 1 : Pretty good! However... While the ipairs()/pairs()
-- iterators *are* pretty handy, they are not *super* fast...
function findDeviceInTracksA(device)
for _, track in pairs(g_song.tracks) do
for _, trackDevice in pairs(track.devices) do
if (rawequal(device, trackDevice)) then
return track
end
end
end
return nil
end
-- Method 2 : Not too bad, but...
-- Repeatedly accessing stuff via tables and looking up the same
-- objects over and over again is redundant and just wastes CPU
-- cycles... No bueno!
function findDeviceInTracksB(device)
for index = 1, #g_song.tracks do
local track = g_song:track(index)
for index2 = 1, #track.devices do
if (rawequal(device, track:device(index2))) then
return track
end
end
end
return nil
end
-- Method 3 : Big pimpin...
-- Local references are best! Fetch them when your function actually
-- needs them, just before any major loops or heavy lifting, just
-- to avoid any repeated unncessary calls later during other critical
-- processes...
function findDeviceInTracksC(device)
-- Local reference to the song...
-- It is tempting to use a "global" song variable here, and then let
-- all of your functions refer back to that, but once again... you
-- should take care, because the song state *may* have changed a lot
-- since the last time your function was called... Better to just
-- grab a fresh local reference before doing any major heavy lifting...
local song = renoise.song()
-- Grab a local ref to the number of tracks in the song, so we can
-- avoid recounting slow tables and other crap within the loop...
-- This particular approach *may* seem ugly, but it is actually a *bit*
-- faster than counting the song track table...
-- (The +1 is for the master track)
local num_tracks = song.sequencer_track_count + song.send_track_count + 1
-- For each track in the song...
for t = 1, num_tracks do
-- Grab a local ref to the track object we wanna mess with...
local track = song:track(t)
-- How many devices we got, yo? (Sadly, we have no other way to
-- count them at the moment, other than checking the table size...
local num_devices = #track.devices
-- For each track device in the track...
for d = 1, num_devices do
-- Is this the device we want?
if (rawequal(device, track:device(d))) then
-- Thats a bingo!
return track
end
end
end
-- Else, womp womp, we found nothing...
return nil
end
-- Run some benchmark test crap...
local test_iterations = 10000
local test_device = renoise.song():track(1):device(1)
local test_functions = { findDeviceInTracksA , findDeviceInTracksB , findDeviceInTracksC }
print(' ')
for t = 1, #test_functions do
local f = test_functions[t]
print('-----------------')
print(string.format("Running test %i...", t))
local start_time = os.clock()
for i = 1, test_iterations do
f(test_device)
end
local stop_time = os.clock() - start_time
print(string.format("%f (%.2f sec)", stop_time, stop_time))
end
-----------------
Running test 1...
0.416992 (0.42 sec)
-----------------
Running test 2...
0.470215 (0.47 sec)
-----------------
Running test 3...
0.121094 (0.12 sec)