Three things I did to tackle Mockingbird performance issues

I posted an update to Mockingbird earlier today that should make loading large projects much faster.  Most of what I did is probably fairly obvious, but I thought I should share what I did anyway for any Cappuccino newbies looking to help the performance of their app.

1. Use the Safari profiler

The first step to optimization is to always, ALWAYS profile your app.  The things slowing you down will often surprise you, and the guys at 280North have done an amazing job to make Safari's profile very usable with Cappuccino.  You can read more about what they've done here.  After using the profiler, I found a few easy ways to speed things up, but no matter what I did, some of the larger projects were still giving me slow script errors on Firefox.  It was at this point that I realized that if I could somehow break up the work that's being done up front when a new project is loaded, things would work out much better.  And then I remembered that I could...

2. Use Javascript's setTimeout method to break up a large chunk of work

The main issue with loading large projects up front is that each page was being recreated serially in one go.  Instead of doing this, I used setTimeout to load each page, and then give the browser a break between each page load.  My function was basically something like:

- (void)loadPageFunction { window.setTimeout(function() { [self loadPageFunction]; }, 0); }

This way, the browser doesn't think that loading all the pages is one giant chunk of work and, so, for large projects, unless the project has a really crazed page, the browser shouldn't complain.

But then, I noticed one more odd thing - whenever I loaded a project after already having a large project, things ran much more slowly.  After using the profiler to see what was going on, I noticed that my app was spending a lot of time cleaning up notifications from the old project.  After some investigation, I found out that the implementation for - [CPNotificationCenter removeObserver:] is way slower than explicitly removing your observer for each notification using - [CPNotificationtCenter removeObserver:name:object:], and I was using the former everywhere.  So, I decided to change these to...

3. Use - [CPNotificationtCenter removeObserver:name:object:] instead of - [CPNotificationCenter removeObserver:]

One of the problems with Javascript is its lack of weak references, which becomes an issue with singletons like CPNotificationCenter's defaultCenter.  I've tried fairly hard to make sure I always remove my observers wherever I set them up, but I'm pretty sure there are still leaks in there somewhere.  These leaks don't really affect performance until you use a - [CPNotificationCenter removeObserver:] call since this will end up iterating through all your notifications (so if you have many, that becomes a problem).  This is, however, a hack around a more fundamental problem - either we need to figure out a way to implement some kind of weak referencing system in Objective-J or, (the easier route), make it easier to not use the default notification center to handle notifications.  A lot of classes internal to the framework use the default notification center for messaging, but it would definitely be a lot easier for me to just trash the notification center whenever I open a new project (still not perfect, I know, but I think better than what currently happens).  

Posted