10 September 2012

IE8 Javascript Performance

Today I was faced with this little peach of a problem in IE8. Horrid performance, as demonstrated by the following IE8 Javascript Profiler shot.


There weren't any noticeable performance problems in IE9 or Chrome, so I figured this was just IE8's slow-boat Javascript performance. I started looking around for problems with jQuery.removeClass() in IE8, but didn't find that much.

Upon further examination, the problem was actually a couple of unintentionally recursive calls. I am using Kendo UI MVVM, using one view model for the entire page, but I also have some fields whose members are calculated by script when the model changes. (I have my reasons!) So, whenever those properties would get updated, the change event on the view model would fire, and the properties would get calculated again, which would then trigger a change event, etc. Since the code was doing deltas on the calculated object, eventually no changes would be made, and the recursion would end. Further testing revealed that the code was getting recursively called about 75 times and 7 seconds too many.

My first thought to fixing this was place conditionals on the change event:

viewModel.bind("change", function(e) {
    if (e.field != "someProperty" && ...) {
        // recalculate
    }
});
But I realized that future-me is unhappy with this solution. This would be a pain to (remember to) update when I added features to this page. So instead, I split the view models. There's one for user-updated fields, and another for manually calculated fields. The calculated fields trigger a recalculate only when the user-updated view model is changed. This nicely got me back down to acceptable performance.


IE8 apparently still hates removeClass, but it's a lot better than before. Note that the same (fixed) code in IE9 runs all removeClass calls combined in 1ms, and the biggest hog only takes 28ms, and the next-largest is 9ms.

Morals of the story:
  • Users should stop using IE (a moral of every web dev story)
  • I should to sanity check JavaScripts with a profiler.

No comments: