📜 ⬆️ ⬇️

User Timing API

This article was written on January 21, 2014 by a member of the HTML5Rocks project, Alex Danilo. I want to note that this is my first translation in Habré, so I'm ready for the minuses, even more to criticism. For all the problems and shortcomings try to write to me personally, I will promptly correct. Original in English .

High-performance web applications are key to the end user. As web applications become more and more complex, understanding performance management is a persuasive programming skill. Over the past couple of years, a lot of new APIs have appeared in the browser that allow you to monitor network performance, download time, etc. but none of them allows you to define quite flexibly and in detail what exactly “slows down” your application. That's what the User Timing API is for , which provides a mechanism for your application to determine what and when it takes time.


You can not optimize that can not be measured


First of all, in speeding up a slow web application, we need to figure out the places that take the most time to complete. Measuring the runtime of individual sections of Javascript code is an ideal way to identify hot spots. This is the first step in finding ways to improve performance. Fortunately, the User Timing API provides a solution with which you can call the API at various places in your code to get detailed time data to help you optimize.
')

High Resolution Time and now () method


The fundamental part of time measurement is accuracy. Previously, we managed with a measurement error of 1 millisecond, but jank-free sites appeared, in which each frame should be drawn within 16 ms. Therefore, for a good analysis of the millisecond error is not enough. But now High Resolution Time has appeared , a new type of synchronization built into modern browsers. High Resolution Time provides us with floating-point timestamps within microseconds, which is a thousand times better than it was before.

To get the current time in the application, you must call the now () method, which is an extension of the Performance interface.

var myTime = window.performance.now(); 

There is another interface called PerformanceTiming , which provides a set of time points related to how a web application is loaded. The now () method returns the time elapsed since the navigationStart of PerformanceTiming .

Type DOMHighResTimeStamp


Previously, to get the current application time, we used the Date.now () function, which returned DOMTimeStamp . DOMTimeStamp returned an integer number of milliseconds. To provide higher accuracy for High Resolution Time , a new type called DOMHighResTimeStamp was introduced. This type already has a floating point, although it also returns the time in milliseconds. But, due to the non-integer value, time contains fractional milliseconds and gives an accuracy of up to one thousandth of a millisecond.

User Timing Interface


Since we now have high-precision time, we can start using User Timing to get time information.

The User Timing interface provides us with functions that can be called in different places of the application to identify weaknesses.

Using mark ()


This method is fundamental to our time analysis toolkit. What does mark () do? This method saves time for future use. But the most useful thing about it is that we can give names to our marks. In this case, the API saves the name-time relationship as a separate element.

Using mark () in various places, you can determine how much time has passed between calls.

The specification provided standard mark names that are fairly understandable, for example: 'mark_fully_loaded', 'mark_fully_visible', 'mark_above_the_fold', etc.

For example, we could make a mark when the application is fully loaded:

 window.performance.mark('mark_fully_loaded'); 

By setting named tags across the entire application, we can collect a huge amount of data and analyze it at our leisure to figure out when and what to spend time on the application.

Measurements and measure () method


After you set the timestamps, you need to calculate the time between them. This is what the measure () method is for. It defines the time that occurred between the marks. It can also determine the time between a user mark and one of the standard ones listed in the Performance Timing interface. For example, to calculate the time from the moment the DOM is loaded until the application is fully loaded, the following code is used:

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

Note: in this example we are passing the reserved name 'domComplete' from the Performance Timing interface.

When the measure () method is called, it saves the result regardless of the labels, so you can get this value later. By keeping separate data about the time when the application is running, it remains responsive and efficient, and you can upload data for analysis after the application does some work.

Reset marks with clearMarks ()


Sometimes it is useful to get rid of a heap of saved marks. For example, you can run an application from the command line, so it will be logical to reset all accumulated marks before launch.

It's easy enough to get rid of the set marks by calling clearMarks ().

Thus, the following code example will reset all existing marks, so that you can re-configure this if you want.

 window.performance.clearMarks(); 

Of course, there are some situations in which you would not want to clear all the marks. If you want to delete specific, then you can just pass the name of the mark to be deleted. For example, as follows:

 window.peformance.clearMarks('mark_fully_loaded'); 

This code gets rid of the mark saved above. But all the other marks will not disappear anywhere.

You may also want to delete the measurements you made. For this there is a corresponding method clearMeasures (). It works just like clearMarks (). For example:

 window.performance.clearMeasures('measure_load_from_dom'); 

will delete the dimension that we saved in the example above. To delete all dimensions, you must call the same method with no arguments, as in clearMarks ().

Saving time data


It's all good to set marks and measure time, but at some point you will want to get this data for analysis. And it's also very simple, all you need is to use the PerformanceTimeline interface.

For example, the getEntriesByType () method allows us to get all the marks or all of our measurements in the form of a list so that we can go through it and process the data. What is convenient is that the list is returned in chronological order, and you can follow the marks as they are called in the application.

The following code:

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

returns all the marks that were marked in the application, while this code:

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

returns all measurements that were taken.

You can also get a list of records by the name you assigned to them:

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

returns a list with one element containing the timestamp 'mark_fully_loaded' in the startTime property.

XHR Request Time Calculation Example


Now that we have a complete picture of the User Timing API, let's determine how long all our XHR requests are executed in the application.

First, we need to change all send () requests to set marks in them, as well as change our callback functions that will set other marks. And also measure the time between them to determine the duration of the request.

Usually, a simple XHR request looks like this:

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

For example, we will add a global counter to keep track of the number of queries and also to use it in saving measurements for each query. The code for this will look like this:

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

This code generates a dimension with a unique name for each XHR request that we send. We assume that all requests are executed sequentially, for parallel requests the code needs to be slightly modified. Therefore, we leave it as a home exercise for readers.

After the web application has made many requests, we can output data to the console with 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 gives you many great tools, ready to use in different parts of your application. Reducing the number of weak points can be easily achieved by placing API calls throughout the application, and post-analyzing the data. But what if your browser doesn't support this API? No problem, you can find a great polyfill here that emulates the API really well and doesn't load the system. What are you waiting for? Try the User Timing API in your applications now to speed them up and users will appreciate it.

Source: https://habr.com/ru/post/210748/


All Articles