R – Cocoa Touch Programming. KVO/KVC in the inner loop is super slow. How to speed things up

iphonekey-value-observingopengl-es

I've become a huge fan of KVO/KVC. I love the way it keeps my MVC architecture clean. However I am not in love with the huge performance hit I incur when I use KVO within the inner rendering loop of the 3D rendering app I'm designing where messages will fire at 60 times per second for each object under observation – potentially hundreds.

What are the tips and tricks for speeding up KVO? Specifically, I am observing a scalar value – not an object – so perhaps the wrapping/unwrapping is killing me. I am also setting up and tearing down observation

[foo addObserver:bar forKeyPath:@"fooKey" options:0 context:NULL];
[foo removeObserver:bar forKeyPath:@"fooKey"];

within the inner loop. Perhaps I'm taking a hit for that.

I really, really, want to keep the huge flexibility KVO provides me. Any speed freaks out there who can lend a hand?

Cheers,
Doug

Best Solution

Objective-C's message dispatch and other features are tuned and pretty fast for what they provide, but they still don't approach the potential of tuned C for computational tasks:

NSNumber *a = [NSNumber numberWithIntegerValue:(b.integerValue + c.integerValue)];

is way slower than:

NSInteger a = b + c;

and nobody actually does math on objects in Objective-C for that reason (well that and the syntax is awful).

The power of Objective-C is that you have a nice expressive message based object system where you can throw away the expensive bits and use pure C when you need to. KVO is one of the expensive bits. I love KVO, I use it all the time. It is computationally expensive, especially when you have lots of observed objects.

An inner loop is that small bit of code you run over and over, anything thing there will be done over and over. It is the place where you should be eliminating OOP features if need be, where you should not be allocating memory, where you should be considering replacing method calls with static inline functions. Even if you somehow manage to get acceptable performance in your rendering loop, it will be much lower performance than if you got all that expensive notification and dispatch logic out of there.

If you really want to try to keep it going with KVO here are a few things you can try to make things go faster:

  1. Switch from automatic to manual KVO in your objects. This may allow you to reduce spurious notifications
  2. Aggregate updates: If your intermediate values over some time interval are not relevant, and you can defer for some amount of time (like the next animation frame) don't post the change, mark that the change needs to posted and wait for a the relevent timer to go off, you might get to avoid a bunch of short lived intermediary updates. You might also use some sort of proxy to aggregate related changes between multiple objects.
  3. Merge observable properties: If you have a large number of properties in one type of object that might change you may be better off making a single "hasChanges" property observe and having the the observer query the properties.