I noticed that there is an allocation/deallocation each and every time a ValueTree callback is triggered - even a propertyChanged! The offending line: ValueTree.cpp:78:
auto listenersCopy = valueTreesWithListeners;
I would guess this is done in case a listener is removed during the callback - fair enough. But perhaps this is worth the tradeoff of avoiding reallocations (most of the time):
listenersCopy.clearQuick();
listenersCopy.addSet (valueTreesWithListeners);
SortedSet<ValueTree*> valueTreesWithListeners, listenersCopy; // line 571, add this member
I’m coming back to this topic as these allocations are now a problem in my app. I have a two mirror ValueTrees, one owned by the main thread and the other by the audio thread. I pipe changes from one to the other using a FIFO in a similar way to juce::ValueTreeSynchroniser.
The children & listeners to the ValueTrees don’t change after initialisation, but properties do, and objects on both sides listen to those changes. This architecture is working well, except of course for the unnecessary allocations.
I have copied the juce::ValueTree class and refined my changes a bit.
static thread_local juce::SortedSet<ValueTree*> treesWithListenersCopy; // near top of file
treesWithListenersCopy.clearQuick(); // line 78
treesWithListenersCopy.addSet (valueTreesWithListeners);
// line 972
if (listeners.isEmpty() && object != nullptr)
{
auto& treesWithListeners = object->valueTreesWithListeners;
treesWithListeners.add (this);
treesWithListenersCopy.ensureStorageAllocated (treesWithListeners.size());
}
Would you consider this as an addition to JUCE, since it is also an optimisation for the general case, avoiding an allocation on every broadcast?