Neil's News

Deferred Analytics

7 April 2010

Google Analytics is a very good statistical analysis service which is widely used by websites that want to keep track of user activity. The downside is that Analytics slows down the loading of each page by about 150ms. A sixth of a second isn't enough for anyone to notice, but when multiplied by a (hypothetical) billion page loads per day, it adds up to one lifetime every fortnight. Let's speed things up a bit and save lives.

Classic Analytics

The classic instructions for adding Analytics provides a code snippet which halts the page load while it pulls in some JavaScript (ga.js) from Google, then pings Google with an invisible image request (_utm.gif). Once both of these requests have occurred, the page can continue loading. Here's a timeline as captured in Firefox 3.0 using Firebug:

[Network activity while loading Google Analytics]
Try it yourself: classic.html

If included at the bottom of a simple page, this may be fine. The browser's throbber[?] will spin for 150ms longer, but the page will be fully visible and usable while Analytics is loading. However more complicated pages often have JavaScript in the onload event (indicated by the red line in the above chart) which needs to execute before the page becomes functional. In these cases, Analytics has slowed down the page load.

Asynchronous Analytics

Google offers a code snippet for asynchronous loading of Analytics. This code essentially forks off the loading of the JavaScript and the image tracker. In theory this means that it is running in parallel with other activity and thus does not delay the page load time. In practice there are a few problems. The first is that if there's nothing else to run in parallel with, there is no advantage in running asynchronously as opposed to blocking; the onload event is delayed the same amount. Here's a timeline of this scenario:

[Network activity while loading Google Analytics]
Try it yourself: async.html

Another issue is that although network activity in a web browser is multi-threaded, the parsing and execution of JavaScript (currently) is not. Notice the large gap between the end of ga.js loading and the start of _utm.gif loading? That's the time it takes to parse and execute the downloaded JavaScript. This work occurs in the browser's main thread model, which means this time is directly added to the page load time. The asynchronous method of calling Analytics does remove the network load time of both elements from the critical path (assuming there is something else to do in the mean time), but that's all.

Deferred Analytics

Here is a method of calling Analytics which adds no penalty at all to the page load time. In the onload function fork off a task for 1 ms later. Due to the single-threaded nature of JavaScript, this doesn't actually mean 1 ms, it means execute once any other onload events have completed. Next asynchronously load and execute the Analytics JavaScript. Pretty simple. In the timeline below notice how much earlier the onload event gets called (the red line):

[Network activity while loading Google Analytics]
Try it yourself: postload.html

In all three methods (classic, asynchronous and deferred) the browser is doing exactly the same work, so the total times are similar. But in the deferred method there is no added delay before the page finishes loading and is ready for user interaction.

Here is the code:

<script type="text/javascript">
  function onload() {
    // Your onload code goes here to initialize your page.
    // Finally, schedule a call for a second round of low-priority initialization.
    window.setTimeout(afterload, 1);
  }

  function afterload() {
    // Called a few ms after the page has loaded.
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  }

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XXXXX-X']);
  _gaq.push(['_trackPageview']);
</script>
<BODY onload="onload()">
...

The lava lamp centrifuge was more popular than I expected. It has received 280,000 views so far, resulting in a saturated Internet connection. I've also been invited to demo it for Yuri's Night at NASA Ames this weekend. See you there!

< Previous | Next >

 
-------------------------------------
Legal yada yada: My views do not necessarily represent those of my employer or my goldfish.