Javascript: Accurate timing is (almost) here

If you’ve ever tried to optimize or locate a bottleneck in your Javascript code you’ve most likely ended up using Date().getTime(). As this function returns the number of milliseconds since the 1st of January, 1970 we can easily determine the total run time of the code we wish to test by taking two readings, one immediately prior to the code to be tested and and one immediately after. A simple subtraction will then give us the total run time in milliseconds:


var start, end;

start = (new Date()).getTime();

// Run a test

end = (new Date()).getTime();

alert(end - start);

While this is easy to use and consistently implemented across browsers it has a three flaws that make it less than ideal for benchmarking – the value returned is an integer so it’s useless for timing anything that takes less than a millisecond, it’s derived from the system clock which may be unstable or subject to adjustment and, depending on the operating system and browser, can be significantly wrong.

With the recent release of Google Chrome 20 to beta we now have the opportunity to use a new, more reliable and more precise timer – High Resolution Time a.k.a.  performance.now. More specifically, we can use the Chrome, vendor-prefixed, version – performance.webkitNow.

As performance.webkitNow returns the number of milliseconds since the navigationStart of the page and is not tied to the system clock we get more reliable timings. Better still, it returns the elapsed time as a double with microseconds in the fractional part, allowing us to have more accurate, sub-millisecond, timings.

If we rewrite the previous example to use High Resolution Time instead of Date().getTime() our code would be:


var start, end;

start = window.performance.webkitNow();

// Run a test

end = window.performance.webkitNow();

alert(end - start);

Running this example would give us results such as 0.005999987479299307 whereas our original, Date-based, example consistently returned zero.

Of course, it’s never so simple. This will work fine in Chrome 20 Beta but will error in all other browsers (and earlier versions of Chrome). To allow for other, as yet to be released, vendor-prefixed versions and to be usable today we must create a small shim function:

 var now = (function() {

// Returns the number of milliseconds elapsed since either the browser navigationStart event or
// the UNIX epoch, depending on availability.
// Where the browser supports 'performance' we use that as it is more accurate (microsoeconds
// will be returned in the fractional part) and more reliable as it does not rely on the system time.
// Where 'performance' is not available, we will fall back to Date().getTime().

var performance = window.performance || {};

performance.now = (function() {
return performance.now    ||
performance.webkitNow     ||
performance.msNow         ||
performance.oNow          ||
performance.mozNow        ||
function() { return new Date().getTime(); };
})();

return performance.now();

});   

This will use the “official” performance.now, if it is available. If not it will try each of the vendor prefixes, then ultimately fall back to Date().getTime(), allowing for all eventualities now or in the near future. Using this function is no more difficult that our previous examples:


var start, end;

start = now();

// Run a test

end = now();

alert(end - start);

 

You can try out this function on jsFiddle or grab it from Gist or Snipplr.

 

More: