performance

Let’s study what tools the browser provides for measuring the performance of programs and functions.

Time to read: 6 min

Briefly

Performance API is a browser API that allows you to measure the execution time of a program using various methods. It uses a very precise type of time measurement – DOMHighResTimeStamp, which operates with an accuracy of up to 5 microseconds (there are a thousand of them in one millisecond).

Example

Creating marks and measurements

Get the time elapsed since the start of navigation to the page

        
          
          const t = performance.now()console.log(t)// 471359
          const t = performance.now()
console.log(t)
// 471359

        
        
          
        
      

Create a named timestamp that stores the time in milliseconds since the start of navigation to the page. This is useful for measuring program performance; for example, you can calculate the difference between marks and determine the function's execution time.

        
          
          performance.mark('application start')console.log(t)
          performance.mark('application start')
console.log(t)

        
        
          
        
      

Calculate the time between two marks:

        
          
          const start = performance.mark('start')const finish = performance.mark('end')performance.measure('total', 'start', 'end')console.log(performance.getEntriesByName('total')[0].duration)// number of milliseconds between the 'start' and 'end' marks
          const start = performance.mark('start')
const finish = performance.mark('end')

performance.measure('total', 'start', 'end')
console.log(performance.getEntriesByName('total')[0].duration)
// number of milliseconds between the 'start' and 'end' marks

        
        
          
        
      

Working with recorded data

Get the list of marks and measurements:

        
          
          for (const entry of performance.getEntries()) {  console.log(`    Entry "${entry.name}", type ${entry.entryType}.    Start at ${entry.startTime}ms, duration ${entry.duration}ms  `)}
          for (const entry of performance.getEntries()) {
  console.log(`
    Entry "${entry.name}", type ${entry.entryType}.
    Start at ${entry.startTime}ms, duration ${entry.duration}ms
  `)
}

        
        
          
        
      

Clear the list of marks and measurements:

        
          
          performance.clearMeasures()performance.clearMarks()
          performance.clearMeasures()
performance.clearMarks()

        
        
          
        
      

Or we can clear everything at once:

        
          
          performance.clearResourceTimings()
          performance.clearResourceTimings()

        
        
          
        
      

How it is written

Creating marks

A mark is the time from the beginning of the transition to the page to the creation of the mark in milliseconds. For example, from clicking on the link or after confirming the entered URL in the search bar.

When creating marks, we can pass a string as the first argument - the name of the mark. Later, we can refer to this name for searching.

        
          
          const markName = 'function execution start'performance.mark(markName)const entries = performance.getEntriesByName(markName)console.log(entries)
          const markName = 'function execution start'
performance.mark(markName)

const entries = performance.getEntriesByName(markName)
console.log(entries)

        
        
          
        
      

The mark object contains the value mark in the entryType field.

Creating measurements

A measurement is the time difference between two marks. A measurement takes several arguments:

  1. Measurement name;
  2. Name of the first mark - optional parameter; if not specified, the first mark will be the time from the start of navigation to the page;
  3. Name of the second mark - optional parameter; if not specified, the second mark will be the call to performance.now() at the moment of measurement creation.

In Firefox and some mobile browsers, calling the measure() method does not return the measurement, and you need to request it manually using getEntriesByName(). Check the support table.

        
          
          const markOne = 'mark_1'const markTwo = 'mark_2'performance.mark(markOne)performance.mark(markTwo)performance.measure('time from navigation start to page')performance.measure('from first mark to now', markOne)performance.measure('time between two marks', markOne, markTwo)const m1 = performance.getEntriesByName('time from navigation start to page')[0]const m2 = performance.getEntriesByName('from first mark to now')[0]const m3 = performance.getEntriesByName('time between two marks')[0]console.log({ m1, m2, m3 })
          const markOne = 'mark_1'
const markTwo = 'mark_2'
performance.mark(markOne)
performance.mark(markTwo)

performance.measure('time from navigation start to page')
performance.measure('from first mark to now', markOne)
performance.measure('time between two marks', markOne, markTwo)


const m1 = performance.getEntriesByName('time from navigation start to page')[0]
const m2 = performance.getEntriesByName('from first mark to now')[0]
const m3 = performance.getEntriesByName('time between two marks')[0]

console.log({ m1, m2, m3 })

        
        
          
        
      

Ways to get marks and measurements

You can get measurements and marks in three different ways:

  1. performance.getEntries() - get a list of all marks and measurements, including those recorded by the browser.
  2. performance.getEntriesByType(type) - get a list of records of the specified type, e.g., mark or measure.
  3. performance.getEntriesByName(name) - get a list of records with the specified name.
More about browser-automatically recorded marks

To enhance the analysis of page performance, the browser automatically records certain marks:

  1. navigation – browser navigation events domComplete, loadEventStart, loadEventEnd, redirectCount, domContentLoadedEventStart, domContentLoadedEventEnd, domInteractive, requestStart, responseStart, unloadEventEnd, unloadEventStart.
  2. resource – contains information about resources loaded by the site. For example, you can learn about the loading of styles or execution of API requests.
  3. paint – information about page rendering, e.g., the time of the first content render – first-paint, first-contentful-paint.

Any method will return an array of entries:

        
          
          const mark = performance.mark('start')const measure = performance.measure('time since start', 'start')const entries = performance.getEntries()const entriesByName = performance.getEntriesByName('time since start')const onlyMarks = performance.getEntriesByType('mark')console.log(entries)console.log(entriesByName)console.log(onlyMarks)
          const mark = performance.mark('start')
const measure = performance.measure('time since start', 'start')
const entries = performance.getEntries()
const entriesByName = performance.getEntriesByName('time since start')
const onlyMarks = performance.getEntriesByType('mark')

console.log(entries)
console.log(entriesByName)
console.log(onlyMarks)

        
        
          
        
      

Ways to clear records

Marks and measurements with the same name do not overwrite each other. If the same name can be used in different parts of the code, for example, if names are created dynamically, it might be useful to remove previously created marks before recording new ones with the same name. You can clear recorded marks and measurements using different methods:

  1. performance.clearMarks(mark_name) - clear all recorded marks with the specified name. If no name is provided, all marks created by the performance.mark() method will be deleted.
  2. performance.clearMeasures(measure_name) - clear all recorded measurements with the specified name. If no name is provided, all measurements created by the performance.measure() method will be deleted.
  3. performance.clearResourceTimings() - clear all marks related to resource loading by the browser.
        
          
          const mark = performance.mark('mark')const measure = performance.measure('measurement')console.log(performance.getEntriesByName('mark').length)// 1performance.clearMarks('mark')performance.clearMeasures('measurement')console.log(performance.getEntriesByName('mark').length)// 0performance.clearResourceTimings()
          const mark = performance.mark('mark')
const measure = performance.measure('measurement')

console.log(performance.getEntriesByName('mark').length)
// 1

performance.clearMarks('mark')
performance.clearMeasures('measurement')

console.log(performance.getEntriesByName('mark').length)
// 0

performance.clearResourceTimings()

        
        
          
        
      

How to understand

When you need to check the speed of code execution, conduct performance tests, or find bottlenecks — the Performance API with its convenient methods and precise measurements comes to the rescue.

The Performance API represents a registry of entries. Entries can be of different types:

  • mark — named timestamp;
  • measure — measurement. Duration between two marks;
  • element — time to load elements;
  • navigation — for entries related to navigation on the site;
  • resource — time to acquire external resources (css, API requests);
  • paint — time of the first render (first paint), or the first render of content (first contentful paint);
  • longtask — time to execute a task from the LongTasks API;

The type of entry is stored in the entryType field. In manual mode, we work with marks and measurements.

In practice

Advice 1

🛠 Conveniently analyze performance using the "Performance" tab in the developer tools. Calls to performance.mark() and performance.measure() will be displayed in the Timings section after profiling is recorded.

Performance debugging panel with performance.mark
Open demo in the new window

🛠 There may be a desire to write a decorator or a wrapper function for performance.mark() and performance.measure() and wrap the entire application in it. For example:

        
          
          const withPerformanceMeasure = (markName, functionToAudit) => {  performance.mark(`${markName}-before`)  functionToAudit()  performance.mark(`${markName}-after`)  performance.measure(`${markName}-before`,`${markName}-after`)}// Script BodywithPerformanceMeasure(myApp)
          const withPerformanceMeasure = (markName, functionToAudit) => {
  performance.mark(`${markName}-before`)
  functionToAudit()
  performance.mark(`${markName}-after`)
  performance.measure(`${markName}-before`,`${markName}-after`)
}

// Script Body

withPerformanceMeasure(myApp)

        
        
          
        
      

This should not be done. The execution cost of the function performance.mark() is minimal, but not zero.

Performance Timings panel for React 17

Advice 2

🛠 performance is useful for finding bottlenecks in your program. Let's consider an example where we have two functions function_1() and function_2() and we want to determine which function is slowing down our program.

Open demo in the new window

When measuring, it is evident that function_1() is working slower, so to speed it up, it needs to be optimized.

🛠 Developer tools allow you to monitor the performance of the program in other ways as well. For example, in the Performance tab, you can record the program's operation and analyze the execution time of individual functions or rendering metrics. In the developer tools settings, you can enable FPS (frames per second) display and check the responsiveness of the interface.