Web Performance: When millisecond resolution just isn’t enough

Sometimes measuring time in millisecond resolution just isn’t accurate enough. Together with industry and community leaders, the W3C Web Performance working group has worked to solve this problem by standardizing the High Resolution Time specification. As of this week, this specification has been published as a Proposed Recommendation (PR) and is widely adopted in modern browsers. Take a look at the What Time is it? test drive demo to see how this API works.

This specification has gone from just an idea to PR in eight short months. The PR stage of standardization is the final step before a Web standard becomes an official W3C Recommendation. Additionally, this interface has been broadly adopted in browsers, including full support in Internet Explorer 10 and Firefox 15, and supported with a prefix in Chrome 22. This is a great example of what’s possible when the industry and community come together through the W3C.

So why aren’t milliseconds good enough? Time has long been measured in the Web platform using some form of the JavaScript Date object, whether it is through the Date.now() method or the DOMTimeStamp type. The Date object represents a time value as time in milliseconds since 01 January, 1970 UTC. For most practical purposes, this definition of time has been sufficient to represent any instant to within 285,616 years from 01 January, 1970 UTC.

For example, at the time of writing this blog, my Date.now() time value from my IE10 Developer Tools Console was 1350509874902. This thirteen digit number represents the number of milliseconds from the origin of this time base, 01 January, 1970. That time corresponds to 17 Oct 2012 21:37:54 UTC.

Though this definition will continue to be genuinely useful for determining the current calendar time, there are some cases where this definition is not sufficient. For example, it is useful for developers to determine if their animation is running smoothly at 60 frames per second (one frame painted every 16.667 milliseconds). Using the simple method of calculating the instantaneous FPS by measuring when the frame drawing callback was last made, one can only determine FPS to 58.8 FPS (1/17) or 62.5 FPS (1/16).

Similarly, sub-millisecond resolution is also desirable when accurately measuring elapsed time (e.g., using the Navigation Timing, Resource Timing and User Timing APIs to instrument your network and script timing) or when attempting to synchronize animation scenes or audio with animation.

To solve this issue, the High Resolution Time specification defines a new time base with at least microsecond resolution (one thousandth of a millisecond). To reduce the number of bits used to represent this number and to increase readability, instead of measuring time from 01 January, 1970 UTC, this new time base measures time from the beginning of navigation of the document, performance.timing.navigationStart.

The specification defines performance.now() as the analogous method to Date.now() for determining the current time in high resolution. The DOMHighResTimeStamp is the analogous type to DOMTimeStamp that defines the high resolution time value.

For example, looking at the current time using performance.now() and Date.now() in the IE10 Developer Tools Console at the time of writing this blog, I see the following two values:

 
performance.now():           196.304879519774
Date.now():        1350509874902

Even though both of these time values represent the same instance in time, they are being measured from a different origin. The performance.now() time value definitely feels more readable.

As High Resolution Time is measured from the start of a document’s navigation, performance.now() in a sub-document will be measured from the start of navigation of the sub-document, not the root document. For example, suppose a document has same-origin iframe A and cross-origin iframe B, where a navigation occurred in iframe A about 5 milliseconds after the start of navigation of the root document and in iframe B about 10 milliseconds after the start of navigation of the root document. If we measure the exact moment of time 15 milliseconds after the start of navigation of the root document, we would get the following values for performance.now() calls in the different contexts:

performance.now() in iframe B:                                5.123 ms

performance.now() in iframe A:                               10.123 ms

performance.now() in root document:                    15.123 ms

Date.now() in any context:                         134639846051 ms

Figure illustrating the difference of how Date.now() measurement compares with performance.now()
Figure: Date.now() is time measured since 01 January 1970, whereas performance.now() is time measured since the start of the document navigation

This design not only ensures there is no data leakage on the time of creation of the parent in cross-origin iframes, it also allows you to measure time relative to your start. For example, if you were using the Resource Timing interface (which uses High Resolution Time) to determine how long it takes for a server to respond to a resource request in a sub-document, you wouldn’t need to make adjustments to take the time of adding the sub-document to the root document into account.

If you wish to do cross frame time comparisons, you would just need to request top.performance.now() to get a time value relative to the root document’s start of navigation, which would return the same value in all same-origin iframes.

Another significant benefit of this API over Date.now() is that performance.now() is monotonically increasing and not subject to clock skew or adjustment. The difference between subsequent calls to performance.now() will never be negative. Date.now() doesn’t have such a guarantee and in practice we have heard of reported cases where negatives time have been seen in analytics data.

High Resolution Time is another great example of how quickly new ideas can become interoperable standards that developers can depend on in modern HTML5-enabled browsers. Thanks to everyone in the W3C Web Performance Working Group for helping design this API and to other browser vendors for rapidly implementing it with an eye towards interoperability.

Thanks,
Jatinder Mann
Internet Explorer Program Manager