Cliff Hacks Things.

Sunday, March 18, 2007

Cocoa Bindings: more on NSArrayController

After my post on NSArrayController's performance yesterday, Scott rightly pointed out that the behavior I was seeing might be a result of how I was using the class, rather than the class's implementation.

I've boiled down the scenario into a bare-minimum project: ArrayGrowDemo.zip (47k)

I've kept the use of Bindings to the textbook case, illustrated below:


All in all, the only unusual thing I'm doing in the code (a slightly simplified version of the Cesta algorithm) is populating the array continuously from a backing thread, but coalescing the KVO notifications in a foreground thread. For what it's worth, I see the same overall pattern by populating from a perform from the main run loop -- the trends just show faster when the population isn't throttled by the performance of the UI.

The application, when run, will write stats in CSV format to /tmp/arraygrow.log. Run it from Xcode, because you'll need that Terminate button handy: the UI becomes totally nonresponsive very quickly. (This replicates the freeze I saw in the Cesta UI when processing a loaded gigabit link, when I was using NSArrayController.)

Here's a graph from one of my test runs, leaving the NSTableView unsorted. The blue line is the amount of time required to distribute KVO notification that the property has changed size (i.e. that the "array" has grown); from Shark profiles, this is where the NSArrayController code tends to slow down.



(More specifically, the blue line is the time spent in didChange:valuesAtIndexes:forKey: per invocation. Its willChange: brother appears to be constant-time. The red line is the time spent in didChange: divided by the number of items actually changed, showing that — fortunately — the NSArrayController isn't linear with regard to the number of changed items...in Tiger....)

I've run a few variations on this, which you can try by editing the Nib. If you change the model key of either table column to "value.value.value.value", you can see the results of binding to a (pathologically) deeply nested property. This appears to slow things down very slightly, and boosts the time spent in the KVO register/unregister code. (I included this because I expected it to slow things down significantly; it doesn't.)

When sorting the table view on either column, each update remains effectively O(n), but the actual time taken increases substantially. (The sort operation, presumably O(n log n), is lost in the O(n) noise.)

Oh kind readers, please take a look at this source and let me know what I'm doing wrong. I would love to switch back to NSArrayController for my packet view in Cesta -- it would reduce the lines of code I have to maintain.

Edit: Updated the images to fit better.

Labels: , ,

3 Comments:

Post a Comment

<< Home