Discovering the High Resolution Time API

In today’s world, performance really matters. Developers need to be able to accurately measure the performance of their software. For those who work on the web, the W3C has a brand new API for reliably keeping time. That API is the High Resolution Time API. This article will explore the High Resolution Time API, and show how to use it.

To measure a function’s performance, web developers used to work with the JavaScript Date.now() method. Typically, time keeping code resembles looks something like this:

var startTime = Date.now();

// A time consuming function
foo();
var test1 = Date.now();

// Another time consuming function
bar();
var test2 = Date.now();

// Print results
console.debug("Test1 time: " + (test1 - startTime));
console.debug("Test2 time: " + (test2 - test1));

The method Date.now() retrieves the current timestamp, based on the system time. Unfortunately, its precision varies between user agents, so it isn’t very reliable. To mitigate this problem, the W3C standardized the High Resolution Time API. The API is described as “a JavaScript interface that provides the current time in sub-millisecond resolution and such that it is not subject to system clock skew or adjustments.” On October 23rd 2012, the specification became a W3C Proposed Recommendation – the final step before becoming a Recommendation. On December 17th they became a W3C Recommendation (updated December 17th)

How the High Resolution Time API Works

I must admit, this is the simplest API I have ever read, as it only consists of a single method. The API extends the Performance interface, which is also used by the Navigation Timing API. If you’ve never heard of it, take a look at Navigation Timing API: How to Profile Page Loads Efficiently.

The only method exposed is now(), which returns a DOMHighResTimeStamp representing the current time in milliseconds. The timestamp is very accurate, with precision to a thousandth of a millisecond. Please note that while Date.now() returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC, performance.now() returns the number of milliseconds, with microseconds in the fractional part, from performance.timing.navigationStart(), the start of navigation of the document, to the performance.now() call. Another important difference between Date.now() and performance.now() is that the latter is monotonically increasing, so the difference between two calls will never be negative.

Maybe you’re wondering how the High Resolution Time API will change your code. The good news is that it won’t change anything. All you need to do is Date.now() with performance.now() to increase the accuracy of your measurements. Taking this into account, the previous code would be rewritten as shown below.

var startTime = performance.now();

// A time consuming function
foo();
var test1 = performance.now();

// Another time consuming function
bar();
var test2 = performance.now();

// Print more accurate results
console.debug("Test1 time: " + (test1 - startTime));
console.debug("Test2 time: " + (test2 - test1));

Compatibility

Currently, very few browsers support the High Resolution Time API. The only desktop browsers that support the API are Internet Explorer 10, Firefox 15+ without prefix, and Chrome from version 20 with its “webkit” prefix (performance.webkitNow()). It seems that Chrome will begin using the unprefixed version starting in version 24. At time of writing, no mobile browsers support this API.

Since the support isn’t wide, the first thing you need is a function to test for browser support and if it is prefixed or not. The following function will return an empty string if the browser uses the unprefixed version of the API. If a prefixed version is used, then the prefix is returned. If the API is not supported, null is returned.

function getPrefix() {
  var prefix = null;
  if (window.performance !== undefined) {
    if (window.performance.now !== undefined)
      prefix = "";
    else {
      var browserPrefixes = ["webkit","moz","ms","o"];
      // Test all vendor prefixes
      for(var i = 0; i < browserPrefixes.length; i++) {
        if (window.performance[browserPrefixes[i] + "Now"] != undefined) {
          prefix = browserPrefixes[i];
          break;
        }
      }
    }
  }
  return prefix;
}

For browsers that do not support the API, a shim is available.

The shim’s author, Tony Gentilcore, is one of the API’s contributors.

In his post, entitled “A better timer for JavaScript,” Gentilcore wrote code that searches for native support first, and uses the Date.getTime() method as a fallback. The code is shown below.

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

Putting it all Together

This section will guide you through a simple demonstration page. The demo will test for browser support first, and then uses a function called doBenchmark that relies on two dummies functions to do a benchmark using the performance.now() method. Please note that I introduced a getTime() function that isn’t related to the API. Its only purpose is to avoid useless repetitions and to have cleaner code. The source code of the demo is shown below.

<!DOCTYPE html>
<html>
  <head>
    <title>High Resolution Time API Test Page</title>
    <script>
      function foo() {
        for(var i = 0; i < 10000000; i++);
      }
      function bar() {
        for(var i = 0; i < 100000000; i++);
      }

      function getPrefix() {
        var prefix = null;
        if (window.performance !== undefined) {
          if (window.performance.now !== undefined)
            prefix = "";
          else {
            var browserPrefixes = ["webkit","moz","ms","o"];
            // Test all vendor prefixes
            for(var i = 0; i < browserPrefixes.length; i++) {
              if (window.performance[browserPrefixes[i] + "Now"] != undefined) {
                prefix = browserPrefixes[i];
                break;
              }
            }
          }
        }
        return prefix;
      }

      function getTime() {
        return (prefix === "") ? window.performance.now() : window.performance[prefix + "Now"]();
      }

      function doBenchmark() {
        if (prefix === null)
          document.getElementById("log").innerHTML = "Your browser does not support High Resolution Time API";
        else {
          var startTime = getTime();
          foo();
          var test1 = getTime();
          bar();
          var test2 = getTime();
          document.getElementById("log").innerHTML += "Test1 time: " + (test1 - startTime) + "<br />";
          document.getElementById("log").innerHTML += "Test2 time: " + (test2 - test1) + "<br />";
        }
      }
      var prefix = getPrefix();
      window.onload = doBenchmark;
    </script>
  </head>
  <body>
    <p id="log"></p>
  </body>
</html>

Conclusion

Throughout this article I showed what the High Resolution Time API is, and how you can use it. As I mentioned, it isn’t widely supported yet, so to accurately test your web applications, you still have a while to wait. However, as you’ve seen, the API is very simple since it consists of a single method. So, once browser support improves, migrating to high resolution time will be quick and painless.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.