EDIT: already well-discussed over at Watch for param value changes - #13 by marc_boule – thanks all!!
EDIT 2: @pachde’s pre-existing solution is written up in his excellent rack-dev-notes.
Hi all–
A very common approach to making Rack modules more CPU-efficient is spin up a bunch of dsp::ClockDividers, call .setDivision(512) or whatever on them in the constructor, and then, in process(), do something like if (lightUpdateDivider.process()) {expensively_update_lights();}.
I’m tuning some dividers for a sequencer module (about which more soon!) and something occurred to me. From the code I’ve seen, many developers are picking divisions from the same “menu” of powers of two (as in in Fundamental, for example). This is fine in isolation–although multiple 2^n dividers will sync up within a module, the maximum expense is likely to be unconcerning for any given module. And when a patch is being built, each new module’s division is starting at an arbitrary point in time modulo each divider, so divisions between modules aren’t going to synchronize.
However, unless I’m missing something, when a patch gets re-loaded, all modules are instantiated at the same time. Therefore, all dividers across all modules in the patch are going to be getting .process() calls in lockstep, in which case the expensive cycles are going to be maximally correlated! If that’s true, some patches might, at least in principle, get CPU spikes/underruns/etc. on reload that they didn’t have during construction, which seems insidious.
Picking from a larger menu, preferably with prime numbers, would be one way to decorrelate. Rack V2 uses 7 for plug lights, presumably for this reason, and 37 for performance measurement to avoid measuring 2^n buffered processors on their output cycles, as seen here. (The comment was a little more explicit in the V1 code, which used 7).
I wonder if the better practice wouldn’t be to randomly set the ClockDivider.clock to something below the division after calling .setDivision in the constructor (since there isn’t a .process(n)). This should basically simulate the normal state of affairs during patch construction. It would be trivial to write an API-compatible DesynchronizedClockDivider that did this automatically as part of its setDivision (and I’ll probably do this in my own module[s]; doesn’t seem as though there would be any downside.)
Has anyone already worried about this, on the forum or elsewhere? Am I missing some existing compensation for it? I don’t have an existence proof of the problem–it’s just theoretical–but I may work one up to confirm.