# Optimisations for RenderingOpsSequenceCalculator

**URL:** <https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795>\
**Category:** General JUCE discussion\
**Created:** [May 6, 2014, 4:40am UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795 "2014-05-06T04:40:35Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 6, 2014, 4:40am UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/1 "2014-05-06T04:40:35Z")

</div>

Hi Jules and everyone, &nbsp;I've attached a version of the AudioProcessorGraph source file with&nbsp;a new method for sorting nodes and made modifications to&nbsp;the calculator code accordingly. &nbsp;On my machine (i7-3770k) I can process around 5000 nodes with about 10,000 connections in roughly 35ms. &nbsp;With my graph arrangement (basic DAW layout) that roughly equates to about 350 tracks with 6 inserts per. &nbsp;It's much faster than I need it to be.&nbsp;

I've attached the code for anyone that would like to give it a test. &nbsp;If anyone has any feedback on the approach or C++'ness of the code it would be greatly appreciated.

Jules if you're interested in using this code in the library, or parts of it or whatever, please feel free.

Notables:

- I'm not a performance guru. &nbsp;Some information is cached and the sorting routine is reasonable. &nbsp;No fancy tricks.
- Tested fairly well with a number of branching configurations. &nbsp;Latencies, detecting feedback loops (sorry still not supported), comparisons of stock calculator statistics. &nbsp;Generally produces the same output as the stock graph. &nbsp;It has a strict sorting routine so there are times when it produces&nbsp;very slightly leaner output. &nbsp;It does a bit worse if you have a node&nbsp;with no connections as it adds them at the beginning vs typically the end with the stock graph. &nbsp;That said, variances on either side appear to be negligible.
- No changes to external apis or structures
- New class GraphMap replaces the ConnectionLookupTable
- New classes MapNode and MapNodeConnection tie&nbsp;Node and Connection information together
- RenderingOpsSequenceCalculator::createRenderingOpsForNode is modified to work with MapNodes and MapNodeConnections&nbsp;and now has an internal struct called ChannelBufferInfo to facilitate the buffer reuse list.
- Feedback loops are still not supported

That's about all I can think of at the moment. &nbsp;Please feel free to give it a try, you should be able to just drop it into your code base with no changes required.

&nbsp;

&nbsp;

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [May 6, 2014, 11:26am UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/2 "2014-05-06T11:26:51Z")

</div>

Thanks for sharing! I'm crazy-busy right now so don't have time to sanity-check it, but will make a note to do so when I get chance!

---

<div class="post-metadata">

**Author:** ![chkn](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/chkn/32/3862_2.png) [@chkn](https://forum.juce.com/u/chkn)\
**Post date:** [May 6, 2014, 12:27pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/3 "2014-05-06T12:27:09Z")

</div>

Maybe somebody wants to have a look on my Multithreaded version, its working in a special case productive environment (but it has no midi-support yet, and no feedback detection)

I don't give up that someday something like this will be in the went&nbsp;into the official juce repo :-)&nbsp;

I thought a while about the Sequence Ordering, but finally it makes no sense&nbsp;(in most cases) for multithread situations, for feedback detection of

cause its needed.

Link:

http://www.juce.com/forum/topic/multithreaded-audioprocessorgraph-source-code

​

&nbsp;

---

<div class="post-metadata">

**Author:** ![jrlanglois](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/jrlanglois/32/21372_2.png) [@jrlanglois](https://forum.juce.com/u/jrlanglois)\
**Post date:** [May 6, 2014, 1:53pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/4 "2014-05-06T13:53:18Z")

</div>

Looks good! Had to fix up&nbsp;some VS warnings in there, and cleared up spurious white-space/trimmed line-ends.  
  
&nbsp;

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 6, 2014, 4:03pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/5 "2014-05-06T16:03:25Z")

</div>

Thanks! &nbsp;That's great to hear&nbsp;and thanks for addressing the warnings and tidying it up!

I&nbsp;noticed in this new version that there is&nbsp;an out of order initialization warning for ChannelBufferInfo so I've updated that. &nbsp;I've also tweaked the MapNodeConnection ownership so that source and destination nodes both point to the same connection instance. &nbsp;It doesn't seem to have much performance effect on my machine but no point in double allocating it. &nbsp;As a result&nbsp;the GraphMap now owns all the MapNodeConnections and the MapNodes just use&nbsp;regular arrays.

The latest&nbsp;version is attached.

&nbsp;

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 6, 2014, 4:27pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/6 "2014-05-06T16:27:22Z")

</div>

You're very welcome Jules. &nbsp;I'm glad to help out where I can. &nbsp;If you have any questions about the code or would like to see some things changed please feel free to get in touch!

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 6, 2014, 4:55pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/7 "2014-05-06T16:55:00Z")

</div>

A couple extra things to mention

- There appears to be a potential bug in stock graph code regarding the totalLatency value. (Note it is \*not\* fixed in the this new version)&nbsp; It considers the last node to be processed with zero outputs to be the determining total latency factor but this is not gauranteed to be the graph's audio output node. &nbsp;If a graph has separate paths that run to different end points this value has the potential to be reported incorrectly.
- Rail Jon Rogut helped me test my initial optimisation attempts ( thanks Rail btw! ) and he mentioned that when testing with large node numbers the duplicate processor check in addNode can slow things down. &nbsp;As such it may be worth providing an option in addNode to bypass the duplicate check for people who have strict graph layouts. &nbsp;Or perhaps a separate&nbsp;"addNodeUnchecked" method. &nbsp;I don't have this issue myself as I run a modified version of the graph which has a number of changes including the removal of the duplicate check.

---

<div class="post-metadata">

**Author:** ![railjonrogut](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/railjonrogut/32/505_2.png) [@railjonrogut](https://forum.juce.com/u/railjonrogut)\
**Post date:** [May 6, 2014, 7:13pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/8 "2014-05-06T19:13:56Z")

</div>

Right,

I had suggested:

```auto
AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor, uint32 nodeId, bool checkForDuplicate /* = true */)
{
    if (newProcessor == nullptr || newProcessor == this)
    {
        jassertfalse;
        return nullptr;
    }

    if (checkForDuplicate)
        {
        for (int i = nodes.size(); --i >= 0;)
            {
            if (nodes.getUnchecked(i)->getProcessor() == newProcessor)
                {
                jassertfalse; // Cannot add the same object to the graph twice!
                return nullptr;
                }
            }
        }
```

with the third parameter which is set to default to true to keep backward compatibility. In my usage there’s no chance of a duplicate, so the check is wasted processing.

Thanks,

Rail

---

<div class="post-metadata">

**Author:** ![railjonrogut](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/railjonrogut/32/505_2.png) [@railjonrogut](https://forum.juce.com/u/railjonrogut)\
**Post date:** [May 7, 2014, 8:35am UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/9 "2014-05-07T08:35:40Z")

</div>

I’m getting a similar assertion as in your very first version from the original thread…

```auto
                    //this seems to hit if a feedback loop was detected in the sorting routine
                    jassert(sourceBuffer != nullptr);
```

It thinks there’s a feedback loop when there isn’t.

Performance is great, the only bottleneck is disconnectNode() where it does a linear search.

Cheers,

Rail

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 7, 2014, 3:12pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/10 "2014-05-07T15:12:07Z")

</div>

Thanks Rail, it turns it's&nbsp;an incorrect assertion so I've removed it. &nbsp;I'll post the updated code to the end of the main&nbsp;thread. &nbsp;disconnectNode is outside of the scope of the work I've done so I can't help much there. &nbsp;Although based on the information you sent me I think it would be better to call "clear" on the graph in that scenario.

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 7, 2014, 3:15pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/11 "2014-05-07T15:15:19Z")

</div>

Latest version of the code with a minor change to remove&nbsp;an incorrect assertion. &nbsp;File is attached.

---

<div class="post-metadata">

**Author:** ![jrlanglois](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/jrlanglois/32/21372_2.png) [@jrlanglois](https://forum.juce.com/u/jrlanglois)\
**Post date:** [May 7, 2014, 4:50pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/12 "2014-05-07T16:50:04Z")

</div>

It would be nice to see this in a fork to monitor progress and&nbsp;list bugs (using GitHub's Issues); it would be a perfect opportunity for those interested to provide direct feedback and patches. Doing so will also avoid any copy/pasta and attachments.&nbsp;:)

---

<div class="post-metadata">

**Author:** ![jrlanglois](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/jrlanglois/32/21372_2.png) [@jrlanglois](https://forum.juce.com/u/jrlanglois)\
**Post date:** [May 8, 2014, 2:57pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/13 "2014-05-08T14:57:00Z")

</div>

Occurs right on Plugin Host's start-up, with default/original nodes. Index is always the same on start-up.  
  
See attached image.

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 8, 2014, 3:39pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/14 "2014-05-08T15:39:07Z")

</div>

hmm, I'm not seeing that here. &nbsp;Maybe a silly question but has the member initialized by the time your breakpoint has hit? &nbsp;(ie. &nbsp;Is the value of&nbsp;midiBufferToUse\_ negative?)

---

<div class="post-metadata">

**Author:** ![jrlanglois](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/jrlanglois/32/21372_2.png) [@jrlanglois](https://forum.juce.com/u/jrlanglois)\
**Post date:** [May 8, 2014, 4:42pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/15 "2014-05-08T16:42:58Z")

</div>

The breakpoint is set at line 213. "midiBufferToUse\_" is indeed negative.

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 8, 2014, 5:03pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/16 "2014-05-08T17:03:33Z")

</div>

Ok, I've noticed that I missed wrapping an else condition in getFreeBuffer, although looking at the code it looks like it should still operate effectively. &nbsp;Regardless, I'll fix that as it's ugly if nothing else. &nbsp;(btw yes I agree with your other post regarding github, this attaching updates to posts is a bit hokey)

If that's not the problem, then it's pretty straightforward to debug. &nbsp;Each call to createRenderOpsForNode ends with new'ing a ProcessBufferOp. &nbsp;If you put a breakpoint at line 817 on startup with no connections it should always go into the first "if" condition to get the midiBufferToUse:

```
/*line 817*/ if (midiSourceConnections.size() == 0)

        {

            // No midi inputs..

            midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi

            if (mapNode->getNode()->getProcessor()->acceptsMidi() || mapNode->getNode()->getProcessor()->producesMidi())

                renderingOps.add (new ClearMidiBufferOp (midiBufferToUse->index));

        }
```

&nbsp;

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 8, 2014, 5:09pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/17 "2014-05-08T17:09:25Z")

</div>

whoops, looks like \*you\* removed the else wrapper. &nbsp;I suppose it was warning about no return value? &nbsp;Anyway, I'll leave it as is!

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 8, 2014, 5:23pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/18 "2014-05-08T17:23:13Z")

</div>

btw, I'll take back the "ugly" remark, your change is better than it was.&nbsp; Although I'd&nbsp;rather have&nbsp;two&nbsp;methods for that, getFreeAudioBuffer and getFreeMidiBuffer... anyway, probably not important.

---

<div class="post-metadata">

**Author:** ![graeme-2](https://avatars.discourse-cdn.com/v4/letter/g/8baadc/32.png) [@graeme-2](https://forum.juce.com/u/graeme-2)\
**Post date:** [May 16, 2014, 6:55pm UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/19 "2014-05-16T18:55:35Z")

</div>

- Fix: &nbsp;Added an additional check to ensure when adding destination nodes that the node isn't already in the "nodesToSort" list (renamed from startNodes to nodesToSort). &nbsp;This bug can cause audio rendering issues and anyone using this code should definitely update.
- Clean up: Removed an unecessary if
- Clean up:&nbsp;Change hard coded feedback limit check to be const class member and upped limit to 50,000
- Additional debugging Info:&nbsp;Added a some additional sanity asserts to the sort routine and additional comments

Latest version is attached.

---

<div class="post-metadata">

**Author:** ![railjonrogut](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/railjonrogut/32/505_2.png) [@railjonrogut](https://forum.juce.com/u/railjonrogut)\
**Post date:** [May 17, 2014, 8:10am UTC](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795/20 "2014-05-17T08:10:42Z")

</div>

Hey Graeme,

This version seems to be working well here….

Adding Nodes and Connections is very fast here now.

I’ll have to figure out a way to improve the removeNode()/disconnectNode() methods 'cause now deleting a track is very slow in the original code.

Thanks!

Rail

[Next page](https://forum.juce.com/t/optimisations-for-renderingopssequencecalculator/12795.md?page=2)
