Supernova, Groups, Threads, Ndef?

I was wondering how supernova manages threads when running several Ndefs in parallel?

I have way more cpu-headroom with supernova that with scsynth but wonder whether I can go any further?

Is there a good strategy in managing Ndefs for optimum multithread distribution with supernova?

cheers, Bernhard

1 Like

Just boosting this thread. Does anyone know of a definitive guide on how to use supernova and ParGroups? I’ve never gotten a good understanding of it.

Oh dear, this thread was overlooked…

I’m not aware of “a guide.” I get the feeling, though, that it’s easy for people to overcomplicate what are, in the end, pretty straightforward principles.

The #1 rule of parallel audio processing is: where there is an output --> input dependency between two processors, they must not be done in parallel.

If you are playing chords (one synth per note) and feeding the mix of these into a reverb unit, then a/ presumably the note synths are not reading signals from each other (no output --> input), so the notes may be calculated in parallel; b/ the mixer depends on the note synths’ output, so it must follow them (in series, not parallel – never parallel for this!) – in SC, Out mixes signals implicitly so you might not need an explicit mixer synth; and c/ the reverb depends on the mixer’s output, so it must also follow in series.

In supernova, the immediate children of a normal Group evaluate in series. The immediate children of a ParGroup evaluate in parallel – they could finish in any order. (Only immediate children – any regular Groups inside a ParGroup will still execute their children in series, but in parallel with other regular Groups inside the ParGroup.)

So the above case would require:

  • Group (to contain this instrument’s nodes)
    1. ParGroup (note synths)
      • Note
      • Note
      • etc.
    2. Reverb synth.

Sent too early… sorry.

Everything in the ParGroup must finish before the reverb can calculate.

It’s up to you to be aware of the relationships between signal processors and model them in a group structure.

Hope that helps…? Doesn’t address the Ndef part of it.

hjh

3 Likes

About Ndef:

AFAIK you can manually move a NodeProxy’s group into another group:

~pargroup = ParGroup(s);

Ndef(\a, { Silent.ar(1) });

Ndef(\a).group.moveToHead(~pargroup);

But…

NodeProxies use InFeedback.

I was a little suspicious about this for parallel processing. As I recalled, InFeedback’s result depends on the order of execution:

  • If the source node for the bus finished processing before the server reaches InFeedback, then InFeedback will output the block of audio being processed now.

  • If the source node comes after the node containing InFeedback, then it will output the previous block of audio.

In both cases, these are the most recently available audio blocks. But, if the order of evaluation is not consistent, then the output may not be consistent.

Let’s test that:

b = Bus.audio(s, 1);

a = { SinOsc.ar(200, 0, 0.1) }.play(outbus: b);

c = { InFeedback.ar(b, 1).dup }.play(target: a, addAction: \addAfter);

(
fork {
	var order = Pseq([\moveBefore, \moveAfter], inf).asStream;
	loop {
		c.perform(order.next, a);
		1.0.wait;
	}
}
)

infeedback-clicks

Oops.

This means you can’t randomly throw Ndefs into ParGroups. If one Ndef reads a signal from another, then you have to make sure that the target Ndef comes later in a serial (regular) Group. (Correctness of the result should be more important than “optimal” usage of multiple cores.)

If you don’t, and if supernova happens to put the source and target Ndefs onto separate cores at roughly the same time, then the target might evaluate InFeedback either before or after the source finishes, and there’s no guarantee of this being consistent. So you might get clicks.

hjh

3 Likes

Thanks for these in-depth responses James. So generally you can have one ParGroup per core on the processor? Is there a way to programmatically query the number of cores available on a system if I wanted to make my code portable for systems with different numbers of cores?

I think this is still a misunderstanding.

It’s the children of ParGroups that run in parallel, not the ParGroups themselves. So there is no relationship at all between number of ParGroups and number of cores.

There’s no restriction on the number of children. Each one of the children will be assigned to a core – if there are more child nodes than cores, then some of them will wait. A ParGroup means that the children may execute in parallel or sequentially as needed; it doesn’t mandate that all children must run in parallel. (Exactly how they are assigned to cores, I’m not sure, but there’s no need for you to handle that detail. It’s your job to make sure that dependencies among synths are modeled correctly in the group structure, and that’s it, nothing else. It’s supernova’s job to run the synths.)

No, because this is not necessary.

Group/ParGroup structure exists solely to model the relationships between signal processors. You’re not responsible at all for triaging core assignment.

hjh

1 Like

Thanks very much James. This helps clear it up for me. I’ll report back with how it works for me.

Would be awesome with a guide in the help system on this!

Reading this again now. This is an excellent explanation, James. Thanks!

this should just be ‘the guide’! awesome, as always, james.