ckv blog: ugen ticking optimization

Posted January 17, 2010

To reduce overhead and boilerplate in the ugens' tick functions, I've just committed code that makes some important changes:

  1. Each ugen's tick function is called exactly once per sample
  2. Rather than return the next sample, a ugen's tick should set self.last
  3. Speaking of which, ugen.last_value is now ugen.last
  4. Calling UGen.initialize_io() is no longer required and it has been removed

So while I previously recommended writing a ugen like this:

Noise = {
  new = function(class, gain)
    return UGen.initialize_io({
      gain = gain or 1.0,
      last_value = 0.0,
      tick = function(self)
        if not(now() == self.last_tick) then
          self.last_value = (math.random() * 2.0 - 1.0) * self.gain;
          self.last_tick = now();
        end
        return self.last_value
      end
    })
  end
}

You can now slim that down to something like this:

Noise = {
  new = function(class, gain)
    return {
      gain = gain or 1.0,
      tick = function(self)
        self.last = (math.random() * 2.0 - 1.0) * self.gain;
      end
    }
  end
}

The changes to how ugens are ticked are not trivial, so please let me know about any bugs I have introduced. :)

The way I ensure that ugens are ticked only once is by sorting the ugen graph by longest path length from speaker & blackhole, and caching the resulting queue until the next time a connection is made or broken. All in all I see a ~20% speed improvement (using ex01.ckv as a benchmark), which I expect to be greater on more complicated graphs, and less on graphs that change frequently.

You can e-mail comments to tom@alltom.com


Home