JS Parse and Execution Time

#

At Velocity NY, Daniel Espeset of Etsy gave a great talk about how Etsy profiles their JavaScript parse and execution time. Even better, after the talk, they released the tool on GitHub.

Daniel shared a few examples in his deck, but I couldn’t wait to take Daniel’s tool and fire it up on a bunch of random browsers and devices that I have sitting around.

For this test, I decided to profile just jQuery 2.1.1, which weighs in at 88kb when minimized. jQuery was selected for its popularity, not because it’s the worst offender. There are many libraries much worse (hey there Angular and your 120kb payload). The results above are based on the median times taken from 20 tests per browser/device combination.

The list of tested devices isn’t exhaustive by any means—I just took some of the ones I have sitting around to try and get a picture of how much parse and execution time would vary.

Parse and execution times of minimized jQuery 2.1.1
Device Browser Median Parse Median Execution Median Total
Blackberry 9650 Default, BB6 171ms 554ms 725ms
UMX U670C Android 2.3.6 Browser 168ms 484ms 652ms
Galaxy S3 Chrome 32 39ms 297ms 336ms
Galaxy S3 UC 8.6 45ms 215ms 260ms
Galaxy S3 Dolphin 10 2ms 222ms 224ms
Kindle Touch Kindle 3.0+ 63ms 132ms 195ms
Geeksphone Peak Firefox 25 51ms 109ms 160ms
Kindle Fire Silk 3.17 16ms 139ms 155ms
Lumia 520 IE10 97ms 56ms 153ms
Nexus 4 Chrome 36 13ms 122ms 135ms
Galaxy S3 Android 4.1.1 Browser 3ms 125ms 128ms
Kindle Paperwhite Kindle 3.0+ 43ms 71ms 114ms
Lumia 920 IE10 70ms 37ms 107ms
Droid X Android 2.3.4 Browser 6ms 96ms 102ms
Nexus 5 Chrome 37 11ms 81ms 92ms
iPod Touch iOS 6 26ms 37ms 63ms
Nexus 5 Firefox 32 20ms 41ms 61ms
Asus X202E IE10 31ms 14ms 45ms
iPad Mini iOS6 16ms 30ms 46ms
Macbook Air (2014) Chrome 37 5ms 29ms 34ms
Macbook Air (2014) Opera 9.8 14ms 5ms 19ms
iPhone 5s iOS 7 2ms 16ms 18ms
Macbook Air (2014) Firefox 31 4ms 10ms 14ms
iPad (4th Gen) iOS 7 1ms 13ms 14ms
iPhone 5s Chrome 37 2ms 8ms 10ms
Macbook Air (2014) Safari 7 1ms 4ms 5ms

As you can see from the table above, even in this small sample size the parsing and execution times varied dramatically from device to device and browser to browser. On powerful devices, like my Macbook Air (2014), parse and execution time was negligible. Powerful mobile devices like the iPhone 5s also fared very well.

But as soon as you moved away from the latest and greatest top-end devices, the ugly truth of JS parse and execution time started to rear its head.

On a Blackberry 9650 (running BB6), the combined time to parse and execute jQuery was a whopping 725ms. My UMX running Android 2.3.6 took 652ms. Before you laugh off this little device running the 2.3.6 browser, it’s worth mentioning I bought this a month ago, brand new. It’s a device actively being sold by a few prepaid networks.

Another interesting note was how significant the impact of hardware has on the timing. The Lumia 520, despite running the same browser as the 920, had a median parse and execution time that was 46% slower than the 920. The Kindle Touch, despite running the same browser as the Paperwhite, was 71% slower than it’s more powerful replacement. How powerful the device was, not just the browser, had a large impact.

This is notable because we’re seeing companies such as Mozilla and Google targeting emerging markets with affordable, low-powered devices that otherwise run modern browsers. Those markets are going to dominate internet growth over the next few years, and affordability is a more necessary feature than a souped up device.

In addition, as the cost of technology cheapens, we’re going to continue seeing an incredibly diverse set of connected devices. With endless new form factors being released (even the Android Wear watches quickly got a Chromium based browser), the adage about not knowing where our sites will end up has never been more true.

The truly frightening thing about these parse and execution times is that this is for the latest version of jQuery, and only the latest version of jQuery. No older versions. No additional plugins or frameworks. According to the latest run of HTTP Archive, the median JS transfer size is 230kb and this test includes just a fraction of that size. I’m not even asking jQuery to actually do anything. Basically, I’m lobbing the browsers a softball here—these are best case results.

This re-affirms what many have been arguing for some time: reducing your dependency on JS is not healthy merely for the minor percentage of people who have JS disabled—it improves the experience for everyone. When anything over 100ms stops feeling instantaneous and anything over 1000ms breaks the users flow, taking 700ms to parse your JavaScript cripples the user experience before it really has a chance to get started.

So what’s a web developer to do?

  1. Use less JavaScript. This is the simple one. Anything you can offload onto HTML or CSS, do it. JavaScript is fun and awesome but it’s also the most brittle layer of the web stack and, as we’ve seen, can seriously impact performance.

  2. Render on the server If you’re using a client-side MVC framework, make sure you pre-render on the server. If you build a client-side MVC framework and you’re not ensuring those templates can easily be rendered on the server as well, you’re being irresponsible. That’s a bug. A bug that impacts performance, stability and reach.

  3. Defer all the scripts. Defer every bit of JavaScript that you can. Get it out of the critical path. When it makes sense, take steps to defer the parsing as well. Google had a great post a few years back about how they reduced startup latency for Gmail. One of the things they did was initially comment out blocks of JavaScript so that it wouldn’t be parsed during page load. The result was a 10x reduction in startup latency. That number is probably different on today’s devices, but the approach still stands.

  4. Cut the mustard. I’m a big fan of “cutting the mustard”, an approach made popular by the BBC. This doesn’t solve the problem of low-end devices with modern browsers, but it will make a better experience for people using less capable browsers. Better yet, by consciously deciding not to overwhelm less capable browsers with excess scripts you not only provide a better experience for those users, but you reduce the need for extra polyfills and frameworks for modern browsers as well. On one recent project where we did this, the entire JavaScript for the site was about 43% of the size of jQuery alone!

There are certainly cases to be made for JS libraries, client-side MVC frameworks, and the like, but providing a quality, performant experience across a variety of devices and browsers requires that we take special care to ensure that the initial rendering is not reliant on them. Frameworks and libraries should be carefully considered additions, not the default.

When you consider the combination of weight, parse time and execution time, it becomes pretty clear that optimizing your JS and reducing your site’s reliance on it is one of the most impactful optimizations you can make.