Iterating over all device parameters performs badly


(bitbear) #1

As I’ve mentioned here, I’m trying to make a plugin that automatically adjusts the quality parameters of all VST plugins in a song. I’m new to both Lua and XRNX development, so I might have gone about this problem the wrong way, since the performance of the below code is horrible. Can anyone spot a problem and nudge me in the right direction? Does a better way to enumerate all plugin parameters exist?

for t = 1, table.getn(renoise.song().tracks) do
  local track = renoise.song().tracks[t]
  print(track.name)

  for d = 1, table.getn(track.devices) do
    local device = track.devices[d]

    print((" %s (%s)"):format(device.name, device.device_path))

    if (device.is_active) then
      for p = 1, table.getn(device.parameters) do
        local parameter = device.parameters[p]

        print((" %s: %d, min(%d), $max(%d), quantum(%d), default(%d)."):format(            
          parameter.name,
          parameter.value,
          parameter.value_min,
          parameter.value_max,
          parameter.value_quantum,
          parameter.value_default
        ))
      end
    end
  end
end

Removing the print statements doesn’t improve the performance. I know this can be an enormous amount of data to go through, but I don’t know how else I’m going to find the device parameters I’m interested in manipulating. I could add if statements inside each loop and not perform parameter iteration on unknown devices, but I would preferably like to list “known devices” in one column and “unknown devices” in another, which still requires me to loop through all parameters of all devices of all tracks. What can be done to optimize this?

Thanks!


(dblue) #2

The [] table-based accessors are fine when used once or twice in your entire script, but they will definitely slow everything down when used inside loops with many iterations.

In general, it’s much better to use the function-based accessors instead.

So for example:
local track = renoise.song():track(t) performs better than local track = renoise.song().tracks[t]
local device = track:device(d) performs better than local device = track.devices[d]
And so on…

Here’s a modified version of your script that performs roughly 9-10 times better overall.

local song = renoise.song()

for t = 1, table.getn(song.tracks) do

  local track = song:track(t)
  
  print(track.name)

  for d = 1, table.getn(track.devices) do

    local device = track:device(d)
    
    print((" %s (%s)"):format(device.name, device.device_path))

    if (device.is_active) then
    
      for p = 1, table.getn(device.parameters) do
    
        local parameter = device:parameter(p)

        print((" %s: %d, min(%d), max(%d), quantum(%d), default(%d)."):format(            
          parameter.name,
          parameter.value,
          parameter.value_min,
          parameter.value_max,
          parameter.value_quantum,
          parameter.value_default
        ))
      end
    end
  end
end

.

Speaking of general performance improvements to this particular tool you’re creating, you might consider keeping an internal plugin cache of some kind.

Once you’ve identified the desired parameters of a particular plugin, you could store the plugin id/name in a table, along with the id/name of each parameter you’re interested in tweaking.

Let’s say that a particular plugin is used 10 times in your song, and it has a “Quality” parameter located at index #123. You would only have to scan it once, save its details to your cache, then refer to your cache for the necessary parameter index #123 on the remaining 9 instances.

Depending on the plugin, this could avoid having to look up hundreds or thousands of duplicate parameters over and over again, since you already know their indexes in advance.