Cliff Hacks Things.

Saturday, March 17, 2007

Cocoa Bindings: mmm, crow

I should have learned this long ago: when I make a glowing blog post about something, I'm about to learn far more about it than I ever wanted to.

I've been collecting Shark profiles of Cesta all day, as my own personal performance-fixit. I've learned two things.
1. My new decode logic is much faster than I anticipated. (Good!)
2. As a result, all of the CPU Cesta's been chewing up has been -- yes, Cocoa Bindings.

The Bindings framework is not to blame. It's the built-in Controller objects (Edit: or, as Scott points out in the comments, the way I'm using them). Specifically, NSArrayController seems to very aggressively make copies of the multi-valued property it controls. I haven't decompiled it for specifics, but I see enormous NSArrays being allocated -- and that's a class that I specifically avoid. (Granted, NSArrayController was probably not designed to be stuffed with thousands of rows per second. Copying the array makes sense for smaller datasets.)

Moreover, for some reason, it keeps invoking indexOfObject:, which is almost by definition O(n/2) for an unsorted array. When my captures start getting large (hundreds of thousands of packets), indexOfObject: and its partner in crime, CFEquals, start creeping up in my profiles.

This is, of course, nonsense. I've been very careful to only append objects to the end of my capture -- it's a time-series data recording, after all. Unless I'm running a "Find" operation, there should be no reason to run around comparing things.

After lunch today I started running into some really odd bugs with NSTreeController, where it appears to be allocating internal NSArrays to keep track of children, and then reading right off the end of them. I know I'm handling my retains and KVO correctly, so this seems to be a flaw in NSTreeController.

So, I've stripped out all the NS*Controller objects. More trouble than they're worth for this application. I've replaced them with lightweight custom implementations that don't call indexOfObject: or crash. I now have an NSTableDataSource and NSOutlineDataSource again -- but they're both very thin, because I had already designed the data model to be presented in tables and trees.

In subsequent profiling, I noticed something odd. Removing NSArrayController and unbinding my packet table resulted in a 400% overall speedup, which I was expecting, but I got another boost in speed from removing NSTreeController, which I was not. It seems to create an NSArrayController-like object behind the scenes, with all its attendant overhead. (Not to mention that both NS*Controllers create callstacks of 40 or so frames, greatly boosting the use of objc_msgSend.)

Thus, my previous post (which read something to the effect of "OMG BINDINGS ARE FAST") has not proven entirely accurate. I would now rephrase that as follows:
1. Bindings, themselves, are fast. I'm still using them extensively to keep my controllers and data sources in sync.
2. NSControllers, the typical use of Bindings, don't seem to scale well to large update frequencies.

(Edit: After discovering someone actually reading this blog, I cleaned up a couple cases of language that may have been overly pointed.)

8 Comments:

  • The fact that it's very easy to get started using these controllers classes also means it's very easy to misuse them.

    I'd encourage you to consider the possibility that minor tweaks in the way you're using them might make huge differences in performance, particularly if you're not (yet) a Cocoa expert. :)

    By Blogger Unknown, at 7:03 PM  

  • Quite possibly true. I went over my setup with the Bindings folks at Cocoaheads last week and they seemed to think I had it right. (They suggested some changes, which unfortunately didn't help in the end.) It's definitely possible that we missed something.

    In my case, I suspect that NSArrayController is simply not intended to deal with unsorted arrays that are growing by thousands of elements per second. (God forbid you apply a filter predicate to it. Even after I optimized the predicate key paths, it would freeze the whole UI.)

    (On the other hand, I'm pretty confident that the NSTreeController weirdness is a bug. I'm trying to isolate it for reporting.)

    By Blogger Cliff L. Biffle, at 11:07 PM  

  • This is an fantastic article.I found it to be very informative.
    Just loved reading this interesting article.

    By Blogger Ronaldo, at 4:25 AM  

  • Get your favorites items online in Auckland Newzealand. Treasurebox is one of the most popular store which provide their customers all the items at lowest price . Buy Mattress nz

    By Blogger unkown, at 4:26 AM  

  • leave a idea for you please visit this pinoy1 tv .here you can watch your daily pinoy tv shows free

    By Blogger Pinoy Swertres, at 4:19 AM  

  • Watch online seriale online and all the other serials online in hd. WE are providing all the romania tv shows and serials on clicksud which you will be watch daily online.

    By Blogger Amania, at 4:34 PM  

  • Clicksud shows: www.pinoy1channel.com/, TVpenet shows, rulare shows, Romanian shows full hd episodes online for free.

    By Blogger swedish, at 12:14 PM  

  • Urmăriți cel mai popular și plăcut seriale Episodul complet zilnic pe Clicksud. Oferim telespectatorilor noștri toate cele mai recente seriale pe care le veți putea urmări în timpul liber. Oamenii adoră să se uite la emisiunile și reluările TV preferate în timpul lor liber. Acum urmăriți și bucurați-vă de seriale online în hd cu subtitrare în engleză. click here

    By Blogger Michal Booster, at 5:37 AM  

Post a Comment

<< Home