CPU headroom, Supernova, multiple servers?

How to use more of your multi-core multi-thread processors resource?

I regularly bump into cpu-overload in SC, although the whole system is barely sweating. I’m on Mac and Linux. As I understand Supernova is for multithreading. Is there a manual or tuto for dummies? I only found the paper from Tim Blechman https://tim.klingt.org/publications/icmc2011_supernova.pdf explaining the concept but not how exactly how to use it. As I understand one should work with groups. But how the groups find to their thread, does Supernova handle this automatically?

I guess in scsynth a work-around for multithreading would be several servers on the same machine? How do they find their thread/core?

What are your strategies for better cpu usage. Manuals, tutorials, stuff to read etc?

You can’t trust the system’s CPU reading here. Most system tuning resources are about getting high data throughput. For real-time audio, the amount of data is much smaller but it’s extremely time sensitive. The system CPU meter isn’t measuring time sensitivity (and it probably averages all the cores – on a 4-core machine, 25% might mean one processor is sweating while the others are not).

Supernova usage is pretty simple.

// before boot:
Server.supernova;

// then boot normally

Synths may be evaluated in parallel if they have no signal dependencies (such as, 4 notes playing a chord). To do that, create a ParGroup.new and put the synths to parallelize into it. Regular groups still calculate in order.

A typical case would be a number of source synths (can be parallel) feeding into an effect synth (which must wait until all the sources are ready). The node tree would look like:

  • Root node (normal group)
    • Default group (normal)
      • ParGroup
        • Synth
        • Synth etc
      • Effect synth

hjh

1 Like

Hi,

you’re not mentioning what kind of code is producing the overload. E.g., a classic is this: piling up synths by not-freeing them. Without any hint it’s difficult to say if supernova would help at all. There are countless possibilities to waste and save CPU by rearranging the code structure, sources of CPU blow-ups can be quite hidden.

You might check these threads also:

Thanks for answers,

I use up to 50 outputs/speakers. The overload happens with to many iterations, to many synths, and with long fadetimes in multichannel Ndefs. In the heat of live performances I get often overload in SC with drops and clicks.

James: I’ll try to understand the parallel group method. If some one knows an example on sccode or github?

How can I chk if the groups are really go to different threads/cores?

Ah, if you’re using Ndef, parallel groups might not help that much (because JITLib keeps its own groups). You might be able to create the Ndefs and move them inside parallel groups.

The thing to be careful about is:

Ndef(\a, something...);

Ndef(\b, something else...);

If \a and \b aren’t sharing signals, then they can evaluate in parallel.

But then in the heat of performance, you do Ndef(\b, { Ndef(\a).ar something ... }). Now \b depends on \a, so it would have to be moved out of – after – the parallel group.

AFAIK JITLib has no automatic mechanism to handle parallelism.

Server.supernova;
s.boot;

g = ParGroup.new;

// reverb
r = {
	var sig = In.ar(0, 2);
	FreeVerb2.ar(sig[0], sig[1])
}.play(g, addAction: \addAfter);

// get a hundred or so nodes at once
p = Pbind(
	\instrument, \default,
	\group, g,
	\dur, 0.02,
	\sustain, 1.5,
	\amp, 0.01,
	\freq, Pexprand(200, 800, inf)
).play;

I’m not quite understanding your question. You put nodes into a ParGroup, just like any other group, except that the ParGroup’s children may be evaluated in parallel on multiple cores. There’s nothing extra special about them.

You can’t (except server CPU reading would go down).

Parallel processing isn’t a magic bullet. Daniel’s suggestions may actually be more helpful.

hjh

Sorry for the semi-side question, but how can one tell an Ndef to create its “internal” Group inside another Group, when you are sure this is what you want? Ndef has .group and .parentGroup members, but I’m not sure how to use them to do this. I’d like the Ndef to create its own internal Group inside another one that I specify (the ParGroup would be a good example). I assumed .parentGroup would do that, but when I try to set it (before or after defining an Ndef), I get:

NodeProxy:parentGroup_ : cannot make non-playing node parentGroup

To answer my own question, the following works to do exactly what I wanted (create an Ndef in a custom Group):

g = ParGroup().isPlaying_(true);
Ndef(\a).parentGroup_(g).source = {SinOsc.ar(440!2) };

But I don’t get why I should have to artificially set “isPlaying” to be true for the Group (or ParGroup) I want as parent for the NodeProxy? I don’t even know what “playing” means for a Group…

Thanks,
Glen.

Good catch.

The isPlaying check is needed for the case of a group that has been freed – the client side still has the Group object, but the node is gone on the server (“not playing”).

Checking the Node class definitions, it looks like isPlaying is not set by default. It’s meant to be used with NodeWatcher: g = ParGroup().register and then the isPlaying flag will not just be set, but also tracked for future state changes.

hjh

Logged as https://github.com/supercollider/supercollider/issues/5194

I think you missed something important in my reply:

IMO this is the better solution.

Changing the current behavior may break user code in ways we can’t predict.

Setting the flag manually means, if the group disappears for some reason, the isPlaying flag will be wrong.

It’s better to register it for state tracking.

hjh

Thanks, I misunderstood. Your expression “good catch” made me think you meant it was a bug. (-;