# Problem with recursion check in AudioProcessorGraph

**URL:** <https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545>\
**Category:** General JUCE discussion\
**Created:** [November 26, 2018, 8:08pm UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545 "2018-11-26T20:08:49Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![dhjdhj](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/dhjdhj/32/39_2.png) [@dhjdhj](https://forum.juce.com/u/dhjdhj)\
**Post date:** [November 26, 2018, 8:08pm UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/1 "2018-11-26T20:08:49Z")

</div>

There’s a nasty problem with Juce 5.4 related to the recursion test implemented in audioProcessorGraph. I don’t think anyone tried that test with a large number of nodes in the graph - the time to check that graph grows exponentially and becomes unusable if you have more and 15 or so nodes.

---

<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:** [November 26, 2018, 9:29pm UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/2 "2018-11-26T21:29:20Z")

</div>

You can check out previous threads:

> [@\[FR\] AudioProcessorGraph addConnections/removeConnections](https://forum.juce.com/t/fr-audioprocessorgraph-addconnections-removeconnections/22982/6):
>
> Okay a report back… I made the changes and it’s dramatically improved the profiling of my plug-in… Since I rarely just make a single connection at a time… I added methods to use OwnedArray::add() instead of OwnedArray::addSorted() and defer buildRenderingSequence() like: bool AudioProcessorGraph::addConnectionUnsorted (const uint32 sourceNodeId, const int sourceChannelIndex, const uint32 destNod…

> [@AudioProcessorGraph && ChangeBroadcaster](https://forum.juce.com/t/audioprocessorgraph-changebroadcaster/23776):
>
> So in my previous thread: I derived AudioProcessorGraph from ChangeBroadcaster and that worked perfectly in 4.2 – but I just tried doing that while testing 4.3.2 and 5.1.1 and if you derive AudioProcessorGraph from ChangeBroadcaster and try and process a block you get an EXC\_BAD\_ACCESS run-time error when it tries to call processAudio (the error location is consistent… but didn’t help to track down the cause of the issue BTW) Any suggestions on how to send a message to the main plug-in Audio…

> [@Chaining AudioProcessorGraphs](https://forum.juce.com/t/chaining-audioprocessorgraphs/12702/7):
>
> I’d like to add a reference to a previous thread which discusses this same issue (slow graph manipulations with a lot of nodes/connections) [https://forum.juce.com/t/audioprocessorgraph-slow-manipulations](https://forum.juce.com/t/audioprocessorgraph-slow-manipulations) Cheers, Rail

Rail

---

<div class="post-metadata">

**Author:** ![dhjdhj](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/dhjdhj/32/39_2.png) [@dhjdhj](https://forum.juce.com/u/dhjdhj)\
**Post date:** [November 26, 2018, 9:44pm UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/3 "2018-11-26T21:44:39Z")

</div>

I’m confused – those threads are from 2014. The issue that I’m talking about is just since Juce 5.4

---

<div class="post-metadata">

**Author:** ![xenakios](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/xenakios/32/4047_2.png) [@xenakios](https://forum.juce.com/u/xenakios)\
**Post date:** [November 26, 2018, 10:19pm UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/4 "2018-11-26T22:19:44Z")

</div>

Perhaps a regression happened and the old exponential time behavior is back again but maybe not because of the same reason…(If I recall right, the old issue was about finding the order in which to reuse audio buffers in the graph.)

---

<div class="post-metadata">

**Author:** ![dhjdhj](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/dhjdhj/32/39_2.png) [@dhjdhj](https://forum.juce.com/u/dhjdhj)\
**Post date:** [November 26, 2018, 11:00pm UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/5 "2018-11-26T23:00:26Z")

</div>

> [@Xenakios](#):
>
> maybe not because of the same reason

As Ralph Waldo Emerson so succinctly put it, “A difference that makes no difference **is** no difference”

Whatever the reason, that class is unusable right now for any significantly sized graph. We had to revert back to the last version. I hope this will get addressed soon.

---

<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:** [November 26, 2018, 11:33pm UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/6 "2018-11-26T23:33:49Z")

</div>

What was your “last version” – can you isolate the commit which caused the change for you… I think there hasn’t been a change on the develop branch to this code for a while.

Rail

---

<div class="post-metadata">

**Author:** ![dhjdhj](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/dhjdhj/32/39_2.png) [@dhjdhj](https://forum.juce.com/u/dhjdhj)\
**Post date:** [November 27, 2018, 12:06am UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/7 "2018-11-27T00:06:21Z")

</div>

We were using 5.2 — since we sell a commercial product (and yes, we have a closed-source license), we’re not in a position to just get every change as soon as it comes out – testing is required etc.  
Previous to that we were using some 4.x version

Switching from 5.2 to 5.4 was way harder than switching from 4.x to 5.2 — some serious refactoring seems to have been done – and we’re seeing some serious bugs in the latest update.  
(For example, opening the AU version of NI’s FM8 shows up in a window that’s 50% larger than the actual plugin)

---

<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:** [November 27, 2018, 12:08am UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/8 "2018-11-27T00:08:59Z")

</div>

The threads I posted go back to 2014… but some of them are from 2017 and were related to changes to the class on the develop branch since 4.x

I have a local modified copy of the class which bypasses the RenderSequenceDouble code in buildRenderingSequence since we only use floats and that change doubled the rendering time from 4.x

Rail

---

<div class="post-metadata">

**Author:** ![dhjdhj](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/dhjdhj/32/39_2.png) [@dhjdhj](https://forum.juce.com/u/dhjdhj)\
**Post date:** [November 27, 2018, 12:17am UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/9 "2018-11-27T00:17:14Z")

</div>

Well, I don’t know what to say - but from running in Xcode, we quickly discovered that execution is getting stuck inside this FOR loop for ages - becomes totally unusable.

```
if (recursionCheck > 0)
    for (auto&& i : dst.inputs)
        if (isAnInputTo (src, *i.otherNode, recursionCheck - 1))
            return true;
```

---

<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:** [November 27, 2018, 12:31am UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/10 "2018-11-27T00:31:33Z")

</div>

I have a reference to the profiling of that method in this post…

> [@AudioProcessorGraph && ChangeBroadcaster](https://forum.juce.com/t/audioprocessorgraph-changebroadcaster/23776/32):
>
> Just so it’s on your radar… the other spots the profiler shows are bottlenecks are in createRenderingOpsForNode() findBufferForInputAudioChannel() and findBufferForInputMidiChannel() Cheers, Rail

Are you running a Debug or Release build… that’s a recursive method so it’ll be extremely slow in a Debug build.

Rail

---

<div class="post-metadata">

**Author:** ![dhjdhj](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/dhjdhj/32/39_2.png) [@dhjdhj](https://forum.juce.com/u/dhjdhj)\
**Post date:** [November 27, 2018, 1:09am UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/11 "2018-11-27T01:09:54Z")

</div>

Yes, I was running a debug build (trying to find out why noise was being produced) when I ran into this issue. I realize debug build is slower than release but it looks like it grows O(n squared) where n is number of edges (connections) and so a debug vs. release build isn’t going to have much impact on it.

We just reverted back to an earlier version of the AudioProcessorGraph

---

<div class="post-metadata">

**Author:** ![JussiNeuralDSP](https://avatars.discourse-cdn.com/v4/letter/j/c68b51/32.png) [@JussiNeuralDSP](https://forum.juce.com/u/JussiNeuralDSP)\
**Post date:** [November 27, 2018, 7:36am UTC](https://forum.juce.com/t/problem-with-recursion-check-in-audioprocessorgraph/30545/12 "2018-11-27T07:36:52Z")

</div>

Yes this is a problem with bigger graph, the time spent on isAnInputTo was ridiculous.

Here’s my fix (did not have time to test this, but similar implementation works):

**juce\_AudioProcessorGraph.h:**

```
typedef HashMap<NodeID,bool> NodeIDBooleanMap;

bool isAnInputTo (Node& src, Node& dst, int recursionCheck, NodeIDBooleanMap &visited) const noexcept;

```

**juce\_AudioProcessorGraph.cpp:**

```
bool AudioProcessorGraph::isAnInputTo (Node& src, Node& dst) const noexcept
{
    jassert (nodes.contains (&src));
    jassert (nodes.contains (&dst));
    
    NodeIDBooleanMap visited;

    return isAnInputTo (src, dst, nodes.size(), visited);
}

bool AudioProcessorGraph::isAnInputTo (Node& src, Node& dst, int recursionCheck, NodeIDBooleanMap &visited) const noexcept
{
    if (visited.contains(dst.nodeID))
        return visited[dst.nodeID];
    
    for (auto&& i : dst.inputs)
        if (i.otherNode == &src)
            return true;

    if (recursionCheck > 0)
        for (auto&& i : dst.inputs)
            if (isAnInputTo (src, *i.otherNode, recursionCheck - 1, visited))
                return true;

    visited.set(dst.nodeID, false);
    
    return false;
}
```
