Is Max/MSP inefficient?
Hi there,
So for last few months I've been designing what I'd call a "basic" synthesizer in Max/MSP (something fairly similar to the feature set of Reason's Subtractor). Everything is going great, except that I am almost finished and the CPU performance is really bad. I've done some basic testing versus other similarly specced synths (by sticking both in as plugins in Live and comparing their CPU usage) and my synth seems 4 or 5 *times* more processor intensive with 5 voices going at once.
Unfortunately, for a variety of reasons, I am not quite ready to to put all the code out there yet (I plan on giving it away for free with the source eventually, I am just not quite ready yet). I know that any responses I get will be greatly hindered by not being able to see exactly what I am working with. But here is a screenshot of the top-level signal chain which I think captures the basic idea pretty well:
The oscillators each have a choice between four waveform types, the lowpassfilter uses a biquad~, the amplifier envelope uses an adsr~, the lfo has three waveforms. I've tried adding and removing each section in turn to identify bottlenecks, but each section uses roughly the same amount of CPU (i.e. there are no bottlenecks/the whole thing is a bottleneck).
That whole signal chain is contained in a poly~ patch (that also contains the interface). The interface sends data into the patches above via pattr. (I believe that pattr is considered inefficient, but the pattr's are only used when the user is actively manipulating the interface, so I don't think it is a big deal).
I guess my questions are the following:
1. Should Max/MSP be able to get the same performance as a similarly specced Synth written in C++ (for example)? Or will Max/MSP simply always be more processor intensive than something written in a lower level language?
2. Is there a blanket way of reducing CPU utilization that I can use in a Synth like this? One idea I found while searching the forums is to down sample the container poly~. But when I tried "down 2" (as an argument to poly~) audio became mangled. I believe this is because the sample rate has to match the dac~ sample rate. I tried to lower the dac~ sample rate, but it was already set to the lowest allowed (44100, further reading makes me believe that the options are available here are the ones allowed by your hardware audio interface).
If there is a way of reducing the total amount of CPU usage used per voice would be great! I know not having the full source is problematic. But I'd greatly appreciate any suggestions you might have.
Thanks in advance! Sorry about the provocative thread title... Max/MSP is a great language in all other respects, hopefully (and likely) I am just doing something wrong here.
> 1. Should Max/MSP be able to get the same performance as a similarly specced Synth written in C++ (for example)? Or will Max/MSP simply always be more processor intensive than something written in a lower level language?
Following my limited insight, the answer would be no. Developing on a lower level allows for decisions that are not available in max. One thing I can think of is the difference between updating signals every sample or once per vector. Having said this,
>
> 2. Is there a blanket way of reducing CPU utilization that I can use in a Synth like this?
Yes. With multiple voices, even a little reduction of cpu in one, can add up to considerable gain. First is to decide what can be removed from the audio chain and find smart ways to do the same with less. Then there are some inefficient objects out there that could be replaced. A good way of testing is to compare two alternatives in a poly with say 100 voices, and look at the difference in cpu.
_
johan
Hi,
(without seeing the patch...)
a simple way is to try playing with settings in:
Options/Dsp status (I/O and signal vector size)
Options/performance options
Be sure to use send/receive not send~/receive~.
Avoid using number boxes in the chain...
But yes, Max/MSP is more processor intensive than something written in
a lower level languages ...
All the best
--
Alessandro Fogar
2008/2/12, Roben Kleene :
>
> Hi there,
>
> So for last few months I've been designing what I'd call a "basic" synthesizer in Max/MSP (something fairly similar to the feature set of Reason's Subtractor). Everything is going great, except that I am almost finished and the CPU performance is really bad. I've done some basic testing versus other similarly specced synths (by sticking both in as plugins in Live and comparing their CPU usage) and my synth seems 4 or 5 *times* more processor intensive with 5 voices going at once.
>
> Unfortunately, for a variety of reasons, I am not quite ready to to put all the code out there yet (I plan on giving it away for free with the source eventually, I am just not quite ready yet). I know that any responses I get will be greatly hindered by not being able to see exactly what I am working with. But here is a screenshot of the top-level signal chain which I think captures the basic idea pretty well:
>
> http://www.1percenter.com/wp-content/uploads/dropbox/signalchain.png
>
> The oscillators each have a choice between four waveform types, the lowpassfilter uses a biquad~, the amplifier envelope uses an adsr~, the lfo has three waveforms. I've tried adding and removing each section in turn to identify bottlenecks, but each section uses roughly the same amount of CPU (i.e. there are no bottlenecks/the whole thing is a bottleneck).
>
> That whole signal chain is contained in a poly~ patch (that also contains the interface). The interface sends data into the patches above via pattr. (I believe that pattr is considered inefficient, but the pattr's are only used when the user is actively manipulating the interface, so I don't think it is a big deal).
>
> I guess my questions are the following:
>
> 1. Should Max/MSP be able to get the same performance as a similarly specced Synth written in C++ (for example)? Or will Max/MSP simply always be more processor intensive than something written in a lower level language?
>
> 2. Is there a blanket way of reducing CPU utilization that I can use in a Synth like this? One idea I found while searching the forums is to down sample the container poly~. But when I tried "down 2" (as an argument to poly~) audio became mangled. I believe this is because the sample rate has to match the dac~ sample rate. I tried to lower the dac~ sample rate, but it was already set to the lowest allowed (44100, further reading makes me believe that the options are available here are the ones allowed by your hardware audio interface).
>
> If there is a way of reducing the total amount of CPU usage used per voice would be great! I know not having the full source is problematic. But I'd greatly appreciate any suggestions you might have.
>
> Thanks in advance! Sorry about the provocative thread title... Max/MSP is a great language in all other respects, hopefully (and likely) I am just doing something wrong here.
>
Thanks for the suggestions.
Johan: I tried out the 100 voices poly~ tip and that works great for seeing if the changes are making a difference.
Alessandro, regarding these ideas:
"Options/Dsp status (I/O and signal vector size)
Options/performance options"
I'll definitely give those a shot. I am curious though, I intend to use it as a VST and then run as a plugin in Ableton Live. Do those settings carry over into the VST or are they only for when the patch is run in the Max app environment?
Thanks again!
Another thing to consider is that when you have things like choices between multiple oscillators, use mute on the unused modules.
This can often be a little bit tricky to set up the logic etc, but I recently did this on a rather CPU expensive drum machine patch and found that it knocked 10%, when I had 12 instances of the same patch going.
Here is an example-
Perhaps some said this already - using poly~ with mute works well for both
efficiency and dynamic allocation of instances. Poly~ instances seem to be
"born" unmuted so it is necessary to mute them at load time.
On 2/12/08 1:46 PM, "Nick Inhofe" wrote:
>
> Another thing to consider is that when you have things like choices between
> multiple oscillators, use mute on the unused modules.
> This can often be a little bit tricky to set up the logic etc, but I recently
> did this on a rather CPU expensive drum machine patch and found that it
> knocked 10%, when I had 12 instances of the same patch going.
>
> Here is an example-
>
> #P toggle 14 361 15 0;
> #P toggle 296 185 15 0;
> #P toggle 228 185 15 0;
> #P toggle 160 185 15 0;
> #P toggle 92 185 15 0;
> #P user gain~ 40 286 24 100 158 0 1.071519 7.94321 10.;
> #P window setfont "Sans Serif" 9.;
> #P window linecount 1;
> #P newex 40 422 49 196617 dac~ 1 2;
> #P number 40 84 35 9 0 4 3 3 0 0 0 221 221 221 222 222 222 0 0 0;
> #P newex 312 184 38 196617 mute~;
> #P newex 244 184 38 196617 mute~;
> #P newex 176 184 38 196617 mute~;
> #P newex 108 184 38 196617 mute~;
> #N vpatcher 696 229 1101 615;
> #P window setfont "Sans Serif" 9.;
> #P window linecount 1;
> #P message 191 222 68 196617 0 1 1 1;
> #P window linecount 0;
> #P newex 191 188 62 196617 prepend set;
> #P outlet 138 228 15 0;
> #P outlet 116 228 15 0;
> #P outlet 94 228 15 0;
> #P outlet 72 228 15 0;
> #P newex 124 57 27 196617 - 1;
> #P newex 72 200 76 196617 unpack 1 1 1 1;
> #P inlet 12 10 15 0;
> #P newex 12 28 32 196617 sel 0;
> #P newex 124 79 27 196617 t b i;
> #P newex 72 158 35 196617 zl rot;
> #P window linecount 1;
> #P message 72 113 43 196617 0 1 1 1;
> #P window linecount 0;
> #P message 12 113 43 196617 1 1 1 1;
> #P comment 112 161 259 196617 unmute the appropriate selection (scales
> easily);
> #P comment 20 90 64 196617 0 = mute all;
> #P connect 7 0 6 0;
> #P connect 6 0 2 0;
> #P fasten 5 0 3 0 129 105 77 105;
> #P connect 3 0 4 0;
> #P connect 4 0 8 0;
> #P fasten 2 0 8 0 17 186 77 186;
> #P connect 8 0 10 0;
> #P connect 8 1 11 0;
> #P fasten 5 1 4 1 146 157 102 157;
> #P connect 8 2 12 0;
> #P fasten 6 1 9 0 39 51 129 51;
> #P connect 9 0 5 0;
> #P connect 8 3 13 0;
> #P fasten 4 0 14 0 77 181 196 181;
> #P connect 14 0 15 0;
> #P pop;
> #P newobj 108 127 215 196617 p muter;
> #N vpatcher 10 59 198 210;
> #P window setfont "Sans Serif" 9.;
> #P newex 50 50 34 196617 saw~;
> #P inlet 74 30 15 0;
> #P inlet 50 30 15 0;
> #P outlet 50 72 15 0;
> #P connect 1 0 3 0;
> #P connect 3 0 0 0;
> #P connect 2 0 3 1;
> #P pop;
> #P newobj 312 225 36 196617 p saw;
> #N vpatcher 10 59 211 195;
> #P window setfont "Sans Serif" 9.;
> #P newex 50 50 53 196617 rect~ 0.5;
> #P inlet 71 30 15 0;
> #P inlet 92 30 15 0;
> #P inlet 50 30 15 0;
> #P outlet 50 72 15 0;
> #P connect 1 0 4 0;
> #P connect 4 0 0 0;
> #P connect 3 0 4 1;
> #P connect 2 0 4 2;
> #P pop;
> #P newobj 244 225 40 196617 p rect;
> #N vpatcher 10 59 179 205;
> #P window setfont "Sans Serif" 9.;
> #P newex 50 50 46 196617 tri~ 0.5;
> #P inlet 50 30 15 0;
> #P inlet 86 30 15 0;
> #P inlet 68 30 15 0;
> #P outlet 50 72 15 0;
> #P connect 3 0 4 0;
> #P connect 4 0 0 0;
> #P connect 1 0 4 1;
> #P connect 2 0 4 2;
> #P pop;
> #P newobj 176 225 40 196617 p tri;
> #N vpatcher 10 59 254 213;
> #P window setfont "Sans Serif" 9.;
> #P newex 50 50 40 196617 cycle~;
> #P inlet 50 30 15 0;
> #P outlet 50 72 15 0;
> #P connect 1 0 2 0;
> #P connect 2 0 0 0;
> #P pop;
> #P newobj 108 225 31 196617 p sin;
> #P newex 22 15 50 196617 sig~ 500;
> #P newex 40 252 282 196617 selector~ 4;
> #P connect 18 0 12 0;
> #P fasten 6 3 17 0 317 175 301 175;
> #P lcolor 6;
> #P fasten 6 3 10 0 317 175 317 175;
> #P lcolor 6;
> #P fasten 6 2 16 0 249 175 233 175;
> #P lcolor 6;
> #P fasten 6 2 9 0 249 175 249 175;
> #P lcolor 6;
> #P fasten 6 1 15 0 181 175 165 175;
> #P lcolor 6;
> #P fasten 6 1 8 0 181 175 181 175;
> #P lcolor 6;
> #P fasten 6 0 14 0 113 175 97 175;
> #P lcolor 6;
> #P fasten 6 0 7 0 113 175 113 175;
> #P lcolor 6;
> #P connect 11 0 0 0;
> #P fasten 11 0 6 0 45 124 113 124;
> #P connect 13 0 12 0;
> #P fasten 13 0 12 1 45 415 84 415;
> #P connect 0 0 13 0;
> #P fasten 1 0 2 0 27 225 113 225;
> #P lcolor 4;
> #P fasten 1 0 3 0 27 224 181 224;
> #P lcolor 4;
> #P fasten 1 0 4 0 27 224 249 224;
> #P lcolor 4;
> #P fasten 1 0 5 0 27 224 317 224;
> #P lcolor 4;
> #P fasten 7 0 2 0 113 213 113 213;
> #P connect 2 0 0 1;
> #P fasten 8 0 3 0 181 212 181 212;
> #P connect 3 0 0 2;
> #P fasten 9 0 4 0 249 212 249 212;
> #P connect 4 0 0 3;
> #P fasten 10 0 5 0 317 211 317 211;
> #P connect 5 0 0 4;
> #P window clipboard copycount 19;
>
Cheers,
Gary Lee Nelson
Oberlin College
www.timara.oberlin.edu/GaryLeeNelson
Roben Kleene schrieb:
> The oscillators each have a choice between four waveform types, the
> lowpassfilter uses a biquad~, the amplifier envelope uses an adsr~,
> the lfo has three waveforms.
If the waveforms~ are using an oscillator each, then this is running
four oscillators, but using only one, same applies for the lfo. You
could also do an lfo in the scheduler...
> 1. Should Max/MSP be able to get the same performance as a similarly
> specced Synth written in C++ (for example)? Or will Max/MSP simply
> always be more processor intensive than something written in a lower
> level language?
In c you have more options to optimize, but the time you need to program
it will be lost, and in the mean time the computers are fast enough
anyway...
> One idea I found while searching the forums is to down sample the
> container poly~. But when I tried "down 2" (as an argument to poly~)
> audio became mangled.
Not a good idea if you want crisp sound...
> If there is a way of reducing the total amount of CPU usage used per
> voice would be great! I know not having the full source is
> problematic. But I'd greatly appreciate any suggestions you might
> have.
No matter how bad you think your implementation is, link to your patch,
the screenshot doesn't scare me, it actually looks as if you do your
stuff carefully... ;-)
I guess also, that you do more than necessary in the signal domain...
Stefan
--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com
Hey Everyone,
Thanks for all the great suggestions. There is a ton of stuff in here for me to try. I've done some preliminary testing with some of the techniques posted here and it looks like I'll be able to get things down a couple of percentage points per voice. This will bring me into the range of 3 or so times the processor of the Synths I was comparing mine to (with 5 voices playing, the other synths are probably written in C). Which is definitely a significant decrease. Taking into account this disadvantage compared to C, there are obviously tremendous advantages to having them written in Max (you can take them apart and put them back together different), so I'd be happy with that decrease.
I've decided to put off writing optimized versions for awhile, since it seems like implementing the optimization solutions will be a significant amount of work, and I want to play with them for awhile first (too much coding and not enough music making).
I do plan on posting them here soon. I've got a couple of hard to pin down (i.e. not consistently reproducible) bugs that maybe a quick look from some of the experts here could help me solve.
Anyway thanks again for all the suggestions, I've definitely got a ton of stuff to try when I decide to tackle optimization. And I'll post again with the full code soon!
-Roben
Roben Kleene schrieb:
> Taking into account this disadvantage compared to C, there are
> obviously tremendous advantages to having them written in Max (you
> can take them apart and put them back together different), so I'd be
> happy with that decrease.
You would also be able to skip parts you don't need for your
application. A general purpose synth will automatically eat up more to
cover all possible cases. It might have a 24 dB filter and you only need
6 dB for example...
> I've decided to put off writing optimized versions for awhile, since
> it seems like implementing the optimization solutions will be a
> significant amount of work, and I want to play with them for awhile
> first (too much coding and not enough music making).
This is a very good strategy. get it working first and optimize later...
> I do plan on posting them here soon.
We look forward to this...
Stefan
--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com
Quote: robenkleene wrote on Tue, 12 February 2008 06:45
----------------------------------------------------
> 1. Should Max/MSP be able to get the same performance as a similarly specced Synth written in C++ (for example)?
----------------------------------------------------
Never.
There is a wide-spread misconception that Max somehow translates a patch into C or C++ or something else that then is magically compiled before execution.
No way.
Each object does it's own thing, in whatever language it was written in, and the Max engine negotiates message passing between objects in real time (as well as scheduling and other stuff). Message passing is pretty expensive.
How expensive? The first incarnation of the Litter objects was in the form of abstractions; when I moved to implementing Litter Power as externals written in C I saw performance improvements in the order of 10-20fold.
Max is incredibly useful as a prototyping tool. It *can* be used to build production-level applications (see Nortron, Radial, etc.). But Max isn't appropriate for production-level for every application in the world. For instance, I wouldn't want to build Max in Max;-)
BTW, the Litter patches are still available and anyone can learn a lot from them. Available at , at the bottom of the page.
Best,
Peter
I'm a total noob, so I don't have much to offer for advice. Just an observation:
A friend of mine has a rather large patch that he has been maintaining and using for quite some time. Until this year, he was running it on a 1ghz G4 powerbook, and it would usually hit just over 60% in the DSP status. Recently, he got a Macbook Pro and it runs much smoother, as can be expected. He sent me a copy of the patch, and I opened it on my own Macbook Pro (Core 2 Duo 2.16ghz). I was surprised to find that it still hit around 30%, even though I have a computer with twice the cores and over double the clockspeed of his old G4. I'm sure this is because Max doesn't use multiple processors, and I don't really care so long as stuff still works. It just surprises me that they haven't optimized the program more (or really, at all) for dual cores.
Quote: mopppish wrote on Thu, 14 February 2008 19:38
----------------------------------------------------
> It just surprises me that they haven't optimized the program more (or really, at all) for dual cores.
----------------------------------------------------
It's easy to say "Oh, hey, they should just optimize for multiple cores, man. Win-win. Obvious."
Doing it is a different matter;-
Also, just running different threads on different processors won't be the big win, I don't think. The largest share of CPU in Max/MSP goes to the audio thread. The biggest CPU win would come from splitting that thread across cores. But it's inherently serial. On top of that, given the current API, MSP doesn't have any way of knowing a priori which perform methods are going to be the CPU hogs. So even if it could split the DSP chain and all its resources into separate threads and sync them appropriately, you might end up with, say, all your cycle~ and *~ objects sitting on one processor using maybe 5% and your fiddle~s and bonk~s and fft~s eating up 95% of the other processor.
For instance.
Since multi-core seems to be the future, I hope & expect that Max will move in that direction. There are ways to tackle the issues sketched above, but I've got a lot of respect for what's involved. In the meantime, you can do a lot by splitting a project into independent standalones. It's extra work but can be worth it.
If any of the Cycling '74 developers wants to add to this, I'm sure there's lots I've left out. But you get the general idea?
Hi,
in Max5, poly~ distributes its subpatchers onto multiple processors,
resulting in a substantial performance improvement in many cases.
All the best
--
Alessandro Fogar
2008/2/14, Eric Sheffield :
>
> I'm a total noob, so I don't have much to offer for advice. Just an observation:
> A friend of mine has a rather large patch that he has been maintaining and using for quite some time. Until this year, he was running it on a 1ghz G4 powerbook, and it would usually hit just over 60% in the DSP status. Recently, he got a Macbook Pro and it runs much smoother, as can be expected. He sent me a copy of the patch, and I opened it on my own Macbook Pro (Core 2 Duo 2.16ghz). I was surprised to find that it still hit around 30%, even though I have a computer with twice the cores and over double the clockspeed of his old G4. I'm sure this is because Max doesn't use multiple processors, and I don't really care so long as stuff still works. It just surprises me that they haven't optimized the program more (or really, at all) for dual cores.
>
>
Oh, don't worry. I'm aware it's not that simple. :)
It's just kind of shocking that leaps and bounds in raw horsepower doesn't always equal as significant of an upgrade in real performance. For someone like me, it's difficult just to rely on the use of poly~ to accomplish that.
On the other hand, assuming that there are ways (even complicated ones) to make it work, Cycling will have to implement them relatively soon. It seems like we're still in the early stages of a phase in technology where the number of processor cores will only continue to increase!
On Feb 14, 2008, at 3:22 PM, Eric Sheffield wrote:
> It seems like we're still in the early stages of a phase in
> technology where the number of processor cores will only continue to
> increase!
If only software technology was progressing in the same direction.
The parallelism that multiple cores require isn't a natural fit for
most processes.
-C
since i was programming in c/c++ many years before i started to play around in max, when it comes to issues of efficiency, i tend to write externals in c, and wrap them in a max patch. it allows me to take care of stuff like midi management, i/o, and a user interface without having to write code for all that, and it reasures me that at least most of the responsibility for performance matters is on my own code.
Peter Castine schrieb:
> BTW, the Litter patches are still available and anyone can learn a
> lot from them. Available at , at
> the bottom of the page.
Not only that, they probably run out of the box on Max 6, and the year
2012 Linux version of Max... ;-)
Stefan
--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com