Looking for a ValueTree property listener post

I’m looking for a post that I thought I saw within the last year or so about an optomised way to listen to single property changes within a ValueTree. It was very similar to an idea I’ve had for a while to keep a separate list of property-only-listeners in the ValueTree and call these alongside the normal listeners. This would mean that ValueTreePropertyValueSource and hence CachedValue wouldn’t incur listener overhead for every single nested tree property change.

The problem is I can’t find that post anywhere now. There is a chance I misread another post in hopeful optimism or even that I imagined it completely.
If anyone can recall it could you please post a link to it so I can take a look. Cheers.

I posted some stuff about a ‘ValueTreeWrapper’ class of mine, but it wasn’t regarding any optimizations, just removing the need for clients to wire up the ValueTree glue, and instead use ‘onChange’ type callbacks for each property.

Perhaps you mean one of the replies to this thread?

I don’t think so… I had already looked through that thread.

However, now that I look over it again maybe what I originally saw was this post [FR] ValueTree::Listener lambdas by pflugshaupt and maybe saw what I already had in mind.

Maybe I’ll just have to code what I had in my head and offer it up.

yeah, his map lookup idea is pretty cool.

Thanks, but it’s only for convenience, not for performance. Even if it was possible to use a HashMap it would still be as slow as using CachedValue with all the nested stuff etc as it doesn’t change the ValueTree listening code at all.

Yeah, my idea was to have something similar to this but directly in ValueTree. That way you’d have a new ValueTree::addPropertyListener (Listener*, const Identifier&) method that listens to a specific property on that tree.

The idea behind this is that you wouldn’t introduce listener overhead for every ValueTree::Listener (which will get a callback for every nested property change). E.g. in our parent EDIT node, if we add a ValueTree::Listener (or CachedValue<> which is essentially the same thing) to one of those properties, the listener callback will be called for literally every property change in the Edit.

I’ve spoken in the past about organising your properties in to appropriately nested hierarchies to avoid this but sometimes it’s the logical structure.

And I’ve also convinced myself not to worry about it as I’ve never actually seen this overhead show up in profiles (almost all CPU usage in our app is inside low-level paint routines or synth/plugin processing code).

However there a nagging feeling I have that sometimes I’m writing code to accommodate the performance characteristics of ValueTree::Listener (by combining actions in to a single, wide, listener callback) rather than what I actually want to write (which is everything having a CachedValue).


Don’t get me wrong though, both callback methods are required and indeed necessary in a lot of places.

I’ve also not fully fleshed out the semantics and performance implications of maintaining another listener list per-ValueTree, per-property. It could be that it’s not worth it and it might be it complicates the class beyond what is acceptable.

It is an idea I’ve been mulling over for a while though and something I’d love to have the time to look in to…

3 Likes

I’m sure there are tons of ValueTree users who would love to be able to do things like

tree.setProperty("someProperty", someValue, nullptr, dontSendNotification);

or even specify who to exclude like

tree.setProperty("someProperty", 
                  someValue, 
                  nullptr, 
                  ValueTree::Listeners::ExcludeChildren); /*or some other way of saying who*/

I think this would be very dangerous as it completely goes against the MVC-type paradigm of using ValueTrees.

It would be absolute chaos if one part of your UI is trying to update itself in response to changes in the tree but other parts are updating it and not letting the listener callbacks propagate.

I can’t see any practical reason why you’d want to do that?


Please note that what I’m suggesting here is a way for listeners to opt-in to only receiving callbacks for a specific property. Semantically this is the same as ValueTree::getPropertyAsValue (id).addListener (this); but under the hood would be optomised in a slightly different way that (perhaps) scales better.

2 Likes

add a DBG message to every one of your valueTreePropertyChanged callbacks and do something in your app that triggers it. Enjoy the flood of callbacks that happen when a single property deep in your nested tree changes.

I like Dave’s suggestion and it feels like the right thing to do, but I have a nagging feeling that the code and data structures which would end up living inside ValueTree to keep track of which listeners are interested in which properties would probably be no better in size or performance than implementing the same thing via an external listener…

i.e. if the ValueTree is responsible for doing the check, then it’ll add the overhead of a name check before all callbacks, but fewer callbacks will end up being invoked. Whereas checking the property name in your listener means less overhead for all callbacks, and potentially quicker name checking (i.e. probably just a straight string compare rather than a list or map lookup), but the callback invocation happens each time. I could sketch out situations where either side is better, but only marginally.

It would make some user code a bit smaller because the callback wouldn’t have to check the name… but TBH either way you have to specify the name, either by passing it as a parameter to addListener, or by checking it when the callback comes in. And there’s something that feels less error-prone about having the name and the callback physically together in the same callback function rather than being far apart. I can imagine myself staring at a callback and wondering why it’s not firing, whereas if your callback fires but you don’t select the right name, that’s something you spot immediately in the debugger.

So… probably could be argued either way, but definitely not a slam-dunk FR as it’s not as simple as it may seem.

@matkatmusic yeah, Dave’s quite right - no way do we ever want to allow people to stop the callbacks firing in ValueTree. I’m sure there are simple use-cases where it’d be fine, but none of the codebases I’ve worked on would be safe with that option around!

Yeah, this is why I haven’t pursued it too much yet. It feels like we’d just be pushing if checks around from callbacks to inside ValueTree.

The main performance benefit would when you change a property in a deeply nested tree as you wouldn’t have to do this every time a property changed and hence call listeners for every parent:

    template <typename Function>
    void callListenersForAllParents (ValueTree::Listener* listenerToExclude, Function fn) const
    {
        for (auto* t = this; t != nullptr; t = t->parent)
            t->callListeners (listenerToExclude, fn);
    }

I think we can agree that there has to be at least one Identifier comparison whether that happens inside the ValueTree or in a callback. Space wise, a listener subclass (such as CachedValue) has to keep the Identifier it’s listening to anyway as it needs to compare to it so moving this inside the ValueTree wouldn’t add any more space if it means it could be left out of the listener class.

The most efficient way I can think of doing this right now is to have a separate set of listeners with a corresponding array of Identifiers they refer to e.g.

ListenerList<Listener> listeners;
Array<Identifier> identifiers;

(Keeping these in sync could be complex and verbose).

Then inside sendPropertyChangeMessage you would have something like (using a C++20 init-for):

    void sendPropertyChangeMessage (const Identifier& property, ValueTree::Listener* listenerToExclude = nullptr)
    {
        ValueTree tree (*this);
        callListenersForAllParents (listenerToExclude, [&] (Listener& l) { l.valueTreePropertyChanged (tree, property); });
        
        for (int i = 0; auto l : valueTreesWithListeners.propertyListeners.listeners.getListeners())
            if (valueTreesWithListeners.propertyListeners.identifiers[i] == property)
                l->propertyChangedCallback (tree, property);
    }

You could then simply ignore the tree and property args if you’ve only registered a single listener.

I think this should be faster overall if you have lots of single property listeners on high-node objects but I’m not a huge fan of obfuscating the listener registration, callback semantics and internals of ValueTree.

I think I’d need some compelling data to show there is a genuine performance improvement for it to be worth it. But that does mean writing a lot of code in a certain style to push it to that demonstrable point in the first place…

As you said, I don’t think valuetree is the right place to do that, I think this could be in ChangeBroadcaster/Listener.

In my work we are using it this way (in Python not C++) and it feels very clean and simple, IMHO this is the way to go!! :stuck_out_tongue:

This might be the post you were looking for: Creating a TreeViewItem tree which mirrors a ValueTree.

I often mull over the concerns that @dave96 raised in this post about ValueTree::Listeners generating a lot of unnecessary traffic with large trees. I’ve been trying to think of ways to make the process more efficient using the tools that already exist, ie. without needing to redesign the ValueTree class, and I’ve come up with a solution that seems like it might work quite well. I haven’t tried it yet–I wanted to float it here in case anyone can see a problem with it that I can’t.

  1. There is only one ValueTree::Listener, listening to the root of the tree.
  2. Every time a node is added, the Root Listener attaches a ReferenceCountedObject to the same node. The ReferenceCountedObject takes a copy of the ValueTree, so it knows where it is in the tree.
  3. Every time the Root Listener detects a property change, it notifies the ReferenceCountedObject which is attached to the same node. The ReferenceCountedObject deals with the event however it needs to.

It seems to me that this would be relatively easy to implement, and might save some CPU. Can anyone see any downsides?

edit (further thoughts):

If there is going to be a one-to-one correspondence between ValueTree nodes and your Components, then the ReferenceCountedObject can be your Component (although ownership might get tricky here).

If the ValueTree represents some other structure which isn’t necessarily going to be displayed, then you can make the ReferenceCountedObject into a ChangeBroadcaster. Other objects can then register and de-register with it by doing something like valueTreeInstance[refCountedObject].getObject()->registerListener(this) (peseudo-code).

I would advise against it. ReferenceCountedObject is made to be easily copyable. Components must not be copyable (since all the backlinks from and to parent Components would break).

TBH. the whole idea seems to me to go down into a hard to maintain flat hierarchy…

Yeah I thought about this too right after I posted (see my edit). Components aren’t such a good idea after all. What do you mean by a “hard to maintain flat hierarchy” though?

I stumbled across this because I’m thinking about the same issue. How about using a Value::Listener and only registering those properties you are interested, something like

valueTree.getPropertyAsValue("id", nullptr).addListener(this);

This way your class will only listen to the values you are interested?