Wednesday, June 13, 2012

A better timer for JavaScript

Browser performance guru, Nat Duca, has introduced high resolution timing to JavaScript. Ready to try in Chrome 20, the experimental window.performance.webkitNow() interface provides microsecond resolution and guarantees not to flow backward (monotonically nondecreasing).

Microseconds matter

Until now, creating a new Date object and querying its time was the only way to measure elapsed time on the web. Depending on the browser and OS, Date's resolution can be as low as 15 milliseconds. One millisecond at best. As John Resig brought to note, that isn't sufficient for benchmarking.

Mono-what-ic?

Perhaps less often considered is that Date, based on system time, isn't ideal for real user monitoring either. Most systems run a daemon which regularly synchronizes the time. It is common for the clock to be tweaked a few milliseconds every 15-20 minutes. At that rate about 1% of 10 second intervals measured would be inaccurate.

Try it out

Like Date, now returns a number in milliseconds. Unlike Date, now's value is:

  • a double with microseconds in the fractional
  • relative to the navigationStart of the page rather than to the UNIX epoch
  • not skewed when the system time changes

If you're using Date for any performance measurement, add this shim to upgrade to performance.now() with a fall back to Date for older browsers.

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

7 comments:

Unknown said...

If you are benchmarking things that take less than a few ms you will find that the power saving logic in the OS will run your program at greatly reduced speed. Your wil be measuring performance that is much lower than a real CPU intensive program will see (where the CPU would be running full speed).

Of course you may be 'lucky' and run your microbenchmark at a moment where the CPU is at full speed for other reasons. So you can see big variation.

It's not enough to plug in your laptop. It is still slowing down the CPU to save power/heat.

Philip said...

Given that performance.now is relative to navigationStart, shouldn't you be subtracting navigationStart from the Date() fallback?

Anonymous said...

@Philip: Good catch! As long as you’re using it to compare two values though, it doesn’t matter.

Sai Prasad said...

If you do not change the window.performance object at all, this shall work:

var now = (function() {
var perf = window.performance || {};
var fn = perf.now || perf.mozNow || perf.webkitNow || perf.msNow || perf.oNow;
// fn.bind will be available in all the browsers that support the advanced window.performance... ;-)
return fn ? fn.bind(perf) : function() { return new Date().getTime(); };
})();

(This will be useful in extension's content scripts & bookmarklets that do not want any change anything on the original page)

tnn said...

It sounds promising.
Can I use that in a Web Worker?

Mark Rejhon said...

Thank you, thank you, thank you -- we needed microsecond precision now.

I've found GPU accelerated web browsers on fast i7 systems are able to execute tasks in sub-millisecond consistencies, so I've been banging my head against the millisecond precision limit. You just help the browser world blow past this millisecond limitation, so this simplifies my life.

Unknown said...

It is really cool.