Group and ParGroup Annotations

I wonder why ServerTools Annotations never had the popularity up to its usefulness, And I also wonder why no one uses ParGroup and supernova in all these groups that process several nodes in parallel.

What happened?

I did try to use supernova for awhile, but quit because:

  • There are little differences in the OSC command implementation. Most of those have been smoothed over (on the language side), but AFAIK the n_order command could produce different orderings between scsynth and supernova.
  • Because of that, the only way my MixerChannel quark could sort mixers according to signal dependencies was by issuing individual n_after commands. Unfortunately, supernova can crash when processing a large sequence of node ordering commands. MixerChannel is central to my usage so that’s a pretty severe blow.
  • Meanwhile, CPUs got fast enough that even my heaviest live sets barely go above 40% CPU, maybe 50%. Usually I’m running around 25-30%. So the risk of crashing isn’t worth it for my use case. Perhaps someone else doing massive additive synthesis would get more benefit from supernova, but for me, it’s a case of, fix the bugs first.

hjh

1 Like

I would love someone to give a clear example of supernova having superior CPU performance. Maybe I just never understood how to use it, but I couldn’t get vastly better cpu performance. I also didn’t try too hard.

Sam

1 Like

I think one difference is that Tim developed supernova on Linux with RT kernel, and he mentioned on his paper problems other than “highly tuned systems”, and not for every dependency graph…

All this should be documented in the Help system, btw.

4.3. Limitations
For low-latency applications real-time computer music sys-
tems require a low worst-case scheduling latency. There-
fore it is not feasible to use blocking primitives for syn-
chronization of the main audio thread and the audio helper
threads. Instead Supernova wakes all helper threads in the
beginning of the main audio callback and all threads poll
a lock-free stack, containing those queue nodes that are
ready for execution. This greedy use of CPU resources is
not friendly to other processes. Depending on the struc-
ture of the node graph a significant amount of CPU time
could be spent in the busy waiting loops. Unless one uses
highly tuned systems running the RT preemption patches
for the Linux kernel, it seems to be the only way to dis-
patch threads quickly enough.

Anecdotally, I see noticeably improved performance on supernova for very complex Synths, e.g. pushing 1000 ugens - that is, without using ParGroup at all. I’ll try to post some concrete measurements next time I’m working on something to test this case.

Yes, an independent benchmark would be quite useful!

There’s a strong UX reason here: managing node ordering for anything even remotely complex in SuperCollider is a huge pain. It can also be quite difficult to debug node ordering problems once you have a running state on the server, especially if it’s relatively active (e.g. with node being added/removed regularly). I suspect that these sort of issues keep most users away from doing anything even slightly complicated with respect to node ordering - or simply punting on the problem completely by using e.g. Ndef. I doubt most people are interested in adding yet another layer of node management (ParGroups) to this, nor take on an even larger surface area for potential failures.

It’s a shame that ordering / grouping in SuperCollider is still a manual problem for the user to solve in code, considering that in the vast majority of cases there’s basically a straightforward, analytically correct solution to node ordering, and there’s very little need for an sclang user to take on this cognitive load.

Well, maybe that’s why I mentioned annotated nodes too.

do you see a solution where there is no cost in latency and the user could just absract node order? (instead of creating an extra layer of proxy-nodes, another strategy more “reactive”, ?)

If you have a directed graph of dependencies - meaning, you know which nodes depend on which other nodes - there’s a straightforward way of ordering those nodes such that the output for each node is ready when it’s needed. This can be a flat list, there’s no need for any structure (e.g. Groups) on the server side at all. This gets slightly more complicated for ParGroups, as there are potentially more solutions and more performance considerations, but it’s still a problem that a user should only be doing optimization work on, rather than manually solving in code.

Here’s a prototype of dependency ordering done by specifying dependencies between groups, though ofc this would be valid between nodes as well: https://gist.github.com/scztt/8d2b2c36e57a1933c59e3cd44a06f02a
This allows expressions of ordering constraints like this, where the final order will be figured out automatically and adjusted on the server.

Gdef(\lastNode, after:\middleNode);
Gdef(\middleNode, after:\firstNode);
Gdef(\firstNode);

I’m working on a more general solution to this - that is, re-implementing of the core Server objects in a way that is declarative and doesn’t require users to handle node ordering or groups at all (probably not even Buses, which are also in many ways just an implementation detail) - but this is a ways off, and may not be easily compatible with existing Server code.

1 Like

It reminds me of the people working on patch categorical theory (darcs and modern derivatives) proving mathematically that most of the operations on git are nonsense )

Maybe we just need a clean framework to get away with most complexities.

SC would be a much more pleasant tool to work with.

MixerChannel has automated node ordering problems for at least 15 years now. (Well, it hasn’t automated things like fx synth order within one channel. But mixer a → main with a post-fader send to an fx channel, fully automated.)

hjh

2 Likes

I hope it is not too off-topic to discuss supernova in general, but the #1 engineering issue with supernova is that plugins that are not specifically supernova-safe can easily crash the server if they try to access a shared resource without doing the appropriate locking ritual. Tim made a valiant effort to mitigate this issue in the built-in plugins, but sc3-plugins and third-party plugins can and do crash supernova when used in ParGroups. The norns community used to use supernova, but had to drop it for scsynth because of this issue. I sadly can’t think of any good solution to this.

1 Like

Nathan, and others, do you hear if tim has been working on supernova?

He doesn’t work on anything in mainline SC for a long time.

There are some commits on his public github repo (a while ago)

Btw one thing that wouldn’t be too hard to support in MixerChannel (though I haven’t done it yet) is to clump mixers with the same “depth” into a ParGroup for each distinct depth.

Currently, you can create a ParGroup inside a mixer’s synthgroup, and source synths would run in parallel, while effects remain in series. Mixers run in series.

Parallelizing mixers with the same depth would process multiple whole-channel chains in parallel. (“Same depth” guarantees no dependency, because of the way depth is calculated, so these would be safe to run in parallel.) E.g., a “main” mixer has a depth of 1. A reverb fx channel feeding into main takes its own default depth and adds the depth from “main,” taking depth 2. A source synth (+1 depth) feeding into main (+1) and with a send to fx (+2) gets depth 4. Sort in descending order and you get source, fx, main, ok! More source mixers → main with an fx send would all have the same depth, so you could parallelize these, and they would all precede the fx channel. (Adding a dependency from one source to another source mixer would increase one channel’s depth, popping it out of the ParGroup.) The depth part, I’ve been using for years; linking that to ParGroups isn’t implemented currently.

If there’s interest, I could look at that. Haven’t done it because I’m not parallel-processing in my own usage, but it might be a nice feature to have. Interest level seems low, though.

hjh

Here are some comments from my side:

but the #1 engineering issue with supernova is that plugins that are not specifically supernova-safe can easily crash the server if they try to access a shared resource without doing the appropriate locking ritual.

While you really should properly lock/unlock busses and buffers, forgetting to do so should not cause a server crash. AFAICT, the worst thing that can happen is that the actual float data is corrupted (e.g. by multiple interleaved concurrent writes). Locking just ensures that individual writes are atomic.

That being said, if plugin authors use the appropriate macros in SC_Unit.h, the difference between scsynth and supernova almost disappears. For example, the popular GET_BUF macro already includes the necessary locking code - which already covers most use cases. (Very few plugins need to access audio or control busses.)

but sc3-plugins and third-party plugins can and do crash supernova when used in ParGroups. The norns community used to use supernova, but had to drop it for scsynth because of this issue.

I would really like to see some evidence for this. Even if that was true, I am wondering why they did not submit a bug report to the sc3-plugins repo…

I sadly can’t think of any good solution to this.

A good first step would be to identify (and fix) broken plugins and report bugs on our issue tracker.

I have fixed several Supernova bugs and inconsistencies in the past, but naturally I can only do this if bugs get reported in the first place…

Yeah sadly Norns development was done without much collaboration with the SuperCollider project. In particular, development was pivoted over one or two very fixable issues without filing bugs or anything afaik. I always felt that was a bummer. The only crash issue that I recall affecting them re Supernova was a clock issue and not related to UGens, but there could have been other things as well.

1 Like