User Timing API

This is a translation of an article by Alex Danilo on the User Timing API published on January 21, 2014.

High web application performance is critical to achieving a good user experience. As web applications become more complex, understanding the impact of performance is vital to creating a competitive user experience. Over the past few years, various APIs have appeared in browsers that allow you to analyze network performance, download time, etc., but they do not provide the necessary information with sufficient flexibility to find problems that slow down the application. Using the User Timing APIprovides a mechanism to determine which part of your application is the slowest. This article will show you how to use the User Timing API and how to use it.

You cannot optimize what you cannot measure

The first step in speeding up your web application is understanding where time is wasted. Measuring the time taken to execute individual parts of the Javascript code is an ideal way to identify hot spots, and this is the first step in understanding how to improve performance. Fortunately, the User Timing API provides a way to embed API calls in different sections of the code, and then get detailed runtime information.

High Resolution time and 'now ()'

Accuracy is the foundation of a true time measurement. Once it was possible to be content with millisecond measurement accuracy, but the development of a site capable of rendering at a speed of 60 FPS means that each frame must be rendered in 16ms. That is, millisecond accuracy is not enough for a qualitative analysis. So High Resolution Time was introduced , a new type of time measurement built into modern browsers. High Resolution Time provides timestamps in floating point format, which allows measurements to be accurate to the microsecond level - thousands of times better than before.
To get the current time in a web application, call the ' now () ' method , an extension to the Performance interface :

var myTime = window.performance.now();

There is another interface - PerformanceTiming , which provides additional information on how the application loads. The 'now ()' method returns the time elapsed since the navigationStart event in PerformanceTiming occurred .

Type DOMHighResTimeStamp

When trying to profile web applications in the past, you would most likely have to deal with something like Date.now (), which returns a DOMTimeStamp . DOMTimeStamp is an integer of milliseconds. To provide higher accuracy, a new type of DOMHighResTimeStamp has been introduced . It is a floating point type, also representing time in milliseconds. But, by virtue of its type, the value can represent fractional parts of milliseconds and makes it possible to obtain accuracy up to one thousandth of a millisecond.

User Timing Interface

Now that you have High Resolution time marks at your disposal, you can use the User Timing interface to get information. The User Timing interface provides functions that allow you to call methods in different places of our application, leaving breadcrumbs like Hansel and Gretel to track where the time costs are.

Using 'mark ()'

The ' mark () ' method is the main tool in the time analysis toolkit. Mark () allows you to save a timestamp. Especially useful is the ability to name timestamps.

Calling mark () in various sections of the code will determine how long it took to reach this mark.
The specification provides a number of predefined tags that may be useful - 'mark_fully_loaded', 'mark_fully_visible', 'mark_above_the_fold' ”, etc.
For example, you can set a label to determine when the application was fully loaded using the following code:

window.performance.mark('mark_fully_loaded');

Setting named labels in a web application can help you collect more runtime information to analyze your existing time costs.

Calculation of measurements with 'measure ()'

Once the timestamps are set, you need to measure the time taken to execute the application between them. The ' measure () ' method is for this purpose intended . It is also able to calculate the elapsed time between timestamps and any known event in the PerformanceTiming interface.
For example, you can measure the time between the full loading of the DOM and the moment when your application is fully loaded:

window.performance.measure('measure_load_from_dom', 'domComplete', 'mark_fully_loaded');

Note: in this example, the event name " domComplete " is passed from the PerformanceTiming interface .

The call to measure () keeps the measurement result available for further retrieval. Saving data during operation, the application remains operational and data can be obtained later, after the end of work.

Removing tags with 'clearMarks ()'

It is sometimes useful to be able to get rid of tags that are already created. For example, you may need this when conducting batch application launches as part of profiling, when you need to reset the results before each launch. By calling ' clearMarks ()' it is easy to get rid of any tags that have been set.
This code will remove all existing marks:

window.performance.clearMarks();

Of course, there are some situations in which you should not delete all timestamps. If you need to get rid of specific tags, you just need to pass the name of the tag you want to delete. For example, the code below will remove the mark set in the first example, leaving all the rest:

window.peformance.clearMarks('mark_fully_loaded');

It may also be necessary to delete previously made measurements, for which there is a corresponding method 'clearMeasures ()'. The principle of operation is similar to clearMarks (), but instead of marks, measurements are deleted. For example, the following code will remove the dimensions set in the previous example about measure ():

window.performance.clearMeasures('measure_load_from_dom');

If you need to delete all dimensions, you should simply call clearMeasures () with no arguments.

Data retrieval

The ability to set marks and measurements is useful, but at some point there will be a need to obtain data for subsequent analysis. This is also easy to do - all you need to do is use the PerformanceTimeline interface .

For example, the method ' getEntriesByType () ' allows you to get all the timestamps or measurements in the form of a list, for subsequent enumeration and selection of information. The list is compiled in chronological order, so the tags are presented in the order they are placed in the web application.
The code below returns a list of all the marks that exist in the application:

var items = window.performance.getEntriesByType('mark');

While the following code returns a list of all dimensions:

var items = window.performance.getEntriesByType('measure');

You can also get a list of named marks. For example, the following code will return a list with one entry containing the mark 'mark_fully_loaded' in the startTime property.

var items = window.performance.getEntriesByName('mark_fully_loaded');

Testing an XHR request (example)

Now, after a brief look at the User Timing API, you can use it to analyze the duration of XMLHttpRequest in the application.

To begin with, you should modify all send () requests to cause the setting of timestamps and replace success callbacks with a function call that sets a different timestamp and then generates a request duration measurement.

Regular XMLHttpRequest will look something like this:

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  do_something(e.responseText);
}
myReq.send();

For the example code, you should add a global counter to track the number of requests, and use it to store measurements for each perfect request:

var reqCount = 0;
var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  window.performance.mark('mark_end_xhr');
  reqCount++;
  window.performance.measure('measure_xhr_' + reqCnt, 'mark_start_xhr', 'mark_end_xhr');
  do_something(e.responseText);
}

This code generates dimensions with unique names for each XMLHttpRequest sent. It is assumed that the queries are executed sequentially - the code for parallel queries will be a little more complicated. This will serve as an exercise for the reader.

After the web application has made all the requests, you can display them in the console using the following code:

var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
  var req = items[i];
  console.log('XHR ' + req.name + ' took ' + req.duration + 'ms');
}

Conclusion

The User Timing API provides many great tools that apply to every aspect of your web application. Reducing the number of “hot spots” in the application can be easily achieved by using API calls throughout the application and post-processing the received data to obtain a picture of delays. But what if the browser does not support this API? This is not a problem, there is a good emulation that can pass the webpagetest.org test . So is it worth the wait? It is worth trying the User Timing API right now, and there will be a new opportunity to achieve greater productivity, and your users will say thanks.

Also popular now: