I need help understanding Gen~ performance

scottbrian's icon

Hi All,

I'm using M4L on Live version 8.3.3 with Max6 version 6.0.5 with the Gen~ option (not sure what version - where do I look?).

I made two sample synths to try to figure out why Gen~ is not working as efficiently for me as using MSP objects.

The first sample (synth4) uses standard MSP objects to implement a filter with resonance. It uses an array of onepole~ objects and a selector~ to chose a 1 pole, 2 pole, 4 pole, or 8 pole configuration. A reson~ object is used for the resonance and a balance control fades between the onepole~ array and the reson~. The synth part is very basic - just enough to make a noise. It is implemented using poly~ with 64 voices. Note that my implementation purposely allows all 64 voices to remain runnnig so I can see the performance numbers. I'm working on a much larger synth that uses only 6 voices and it is having some serious performance issues, so I had hoped to use Gen~ to improve things.

The second sample (synth5) uses the same synth parts, but the filter is constructed using gen~. I found some example code for the onepole and the reson in the examples folder and modified it make what I think is the equivalent configuration as the one in synth4.

I run the synths in M4L. For Synth4, Live shows 28% CPU. For Synth5, Live shows 62% CPU.

I'm new to Max/MSP and very new to using Gen~. I will appreciate any comments on anything I might be doing wrong that is causing the 62% CPU. I was hoping to use Gen~ to cut my CPU to make it a lot smaller that 28%, but I'm afraid I don't understand enough to know what I'm doing wrong. I have another filter design using MSP lores~ and Gen~ (again from the examples) and the MSP version uses 22% CPU while the Gen~ version uses 60%. I can post that one too if need be.

I'm not sure whether the copy compressed gets everything copied, so I'll copy all of the separate pieces.

Max Patch
Copy patch and select New From Clipboard in Max.

First, synth4 stuff:
SBT_TestGenSynth04.amxd

Max Patch
Copy patch and select New From Clipboard in Max.

SBT_TestGen4.maxpat (for the poly~):

And here's synth5 using gen~ filter stuff:

Max Patch
Copy patch and select New From Clipboard in Max.

SBT_TestGenSynth05.amxd

Max Patch
Copy patch and select New From Clipboard in Max.

SBT_TestGen5.maxpat (for the poly~):

And, the three Gen~ parts:

Max Patch
Copy patch and select New From Clipboard in Max.

SBT_OnePoleArray_01.gendsp

Max Patch
Copy patch and select New From Clipboard in Max.

SBT_Reson_01.gendsp (this one is pretty much a straight copy from the examples folder):

Max Patch
Copy patch and select New From Clipboard in Max.

SBT_2_x_1_mixer_01.gendsp

Graham Wakefield's icon

Hi there,

I tried both MSP and Gen versions here. MSP is giving me 52% CPU, Gen about 57%. I also tried the same patcher with the gen~ objects removed (just a straight pass-through), which runs at about 27% here..., so the real cost of MSP is 25%, and Gen is 30%. This is using 44100 sampling rate, 256 vector & IO size, and both overdrive and scheduler in audio interrupt are off. I'm running a development version of 6.0.7, so perhaps some of the fixes since 6.0.5 have improved the situation.

However, what needs to be borne in mind is that Gen and MSP operators are not always equivalent; in many cases the Gen operators are more precise.

For example, the MSP onepole~ object does not calculate frequency at sample rate; even if a signal is attached, the frequency calculation is performed at block rate. The same applies to the frequency / Q calculations of reson~. I believe this accounts for the main differences you are seeing. In the Gen code, all of these are being calculated at full sample rate. In your patch you won't be able to hear any difference though, since the inputs are being converted to signals using [sig~]. It would make more sense to remove the [sig~] conversions and turn the gen inputs into [param] objects instead.

(There are other cases where Gen operators are more precise than MSP objects. For example, [sin] in gen~ uses 64-bit precision, but [sinx~] in MSP still uses 32-bit precision.) A direct comparison is not always reasonable.

Furthermore, the performance of gen~ will improve the more operators are included in a single gen patcher; with just a single object (e.g. your mixer) you're paying more for the overhead of gen~ itself. It makes sense to combine the onepole, reson and mix patchers into a single gen~ (you'd have to remove the history operator names for this to work properly... but you don't need to name them anyway).

There are some features in the soon to be released update Max 6.0.7 which will improve this situation even more...

Hope this helps,

Graham

scottbrian's icon

Hi Graham,
Thanks for trying the sample synths and for your informative reply. I'll wait for 6.0.7 and try the test again. In the mean time, I turned off Overdrive (options menu), "Scheduler in Overdrive" (was already off), and "in Audio Interrupt" (was already off), and set "I/O Vector Size" and "Signal Vector Size" to 256 (but, not sure that does anything because Audio is OFF - I'm going through M4L through Live which is going through Avid HDX). But, good news is that my CPU went down ever so slightly to 25% for Synth4 and 60% for Synth5. Hopefully, the 6.0.7 will have a more pronounced reduction in CPU.

My next immediate steps will be to continue to learn more about Gen and learn how to implement the [param] objects. One requirement I have with my filter is to be able to sweep the cutoff using an LFO signal, and I'm not sure yet whether [param] is a static input or dynamic input, and if dynamic whether it is more effecient to use that or the LFO signal directly (yes, I have much to learn).

Thanks also for explanations regarding precision and your advice on reducing the Gen overhead by combining my smaller patches - I will start restructuring things.

Regards,
Scott