Andy Edinborough

CSS Stress Testing and Performance Profiling

I present you with the CSS Stress Test bookmarklet.

Now let me explain: I have been losing my sanity over the oddest issue.  The project I’m working on right now has a fairly complex stylesheet.  Performance for the site is absolutely critical.  I’ve done my best to squeeze and optimize every line I can.  In all browsers, it runs like a champion.  Except IE9.  It runs terribly in IE9.  To make matters worse, it runs fine in IE8, IE7, and though it’s mangled, it even runs in IE6!

Something specific to IE9 was causing a serious performance glitch.  I realized fairly quickly that CSS was to blame.  Disabling JavaScript had no effect, but disabling all CSS instantly fixed performance.  I went through the usual suspects: filters, and various new CSS3 properties, but nothing seemed to help.  So I reached out to the community looking for something that could profile my CSS rules and find what was killing performance. 

Sadly, no one had an answer.  :[

So, as I lay there in the corner of the closet, weeping bitterly, I realized that writing my own CSS stress tester wouldn’t be that difficult.  All it needs to do is stress the page for a baseline (in my case, all I needed to do was scroll the window up and down), index all the available CSS class names, and then methodically remove one, stress the page, and compare that time to the baseline.

Of course it took a while to work out the exact implementation. 

My first implementation was written synchronously.  It indexed the classes, scrolled the page up and down and tracked times.  It turns out, browsers don’t like you doing really annoying things (like franticly scrolling the page).  So after a couple iterations window.scrollBy stopped responding to my commands.

Instead of:

var time0 = new Date().getTime();
window.scrollBy(0,100);
var delta = (new Date().getTime()) - time0;

I needed to add an event handler to window.scroll, scroll the page, then capture time in the event.  Of course this means the entire set of code would have to be rewritten to run asynchronously:

var time0 = new Date().getTime(),
   recordTime = function(){
        var delta = (new Date().getTime()) - time0;
        window.removeEventListener('scroll', recordTime, false);
    };
window.addEventListener('scroll, recordTime, false);
window.scrollBy(0, 100);

(this isn’t how the code is actually written—just showing some insight into the how it works)

Fast forward a several weeks.

I’ve created a project on GitHub for the completed code (http://github.com/andyedinborough/stress-css), and have created a simple bookmarklet to invoke the test: CSS Stress Test

This test pinpointed exactly which class was killing the page.  From there, I was able to fiddle with the CSS properties of the class and nail down the culprit.

 

Was it box-shadow?  … nope

 

Was it filter/opacity? … nope

 

…. wait for it …

 

It was border-radius!  More specifically, it was border-radius on a body-level element.  I use border-radius quite extensively on the page, and the only time it causes performance problems in on a body-level element with a large number child elements.