performance

Изучаем, какие инструменты предоставляет браузер для измерения времени работы программ и функций.

Время чтения: 5 мин

Кратко

Скопировано

Performance API — это API браузера, позволяющее измерять время работы программы при помощи различных методов. Для этого используется очень точный тип измерения времени – DOMHighResTimeStamp, работающий с точностью до 5 микросекунд (в одной миллисекунде их тысяча).

Пример

Скопировано

Создание меток и измерений

Скопировано

Получаем время, прошедшее с начала навигации на страницу

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

        
        
          
        
      

Создаём именованную метку времени, сохраняющую время в миллисекундах с начала навигации на страницу. Это полезно для измерения работы программы, например, можно вычислить разницу между метками и узнать время работы функции.

        
          
          performance.mark('старт приложения')console.log(t)
          performance.mark('старт приложения')
console.log(t)

        
        
          
        
      

Вычисляем время между двумя метками:

        
          
          const start = performance.mark('начало')const finish = performance.mark('конец')performance.measure('итого', 'начало', 'конец')console.log(performance.getEntriesByName('итого')[0].duration)// количество миллисекунд между метками 'начало' и 'конец'
          const start = performance.mark('начало')
const finish = performance.mark('конец')

performance.measure('итого', 'начало', 'конец')
console.log(performance.getEntriesByName('итого')[0].duration)
// количество миллисекунд между метками 'начало' и 'конец'

        
        
          
        
      

Работа с записанными данными

Скопировано

Получаем список меток и измерений:

        
          
          for (const entry of performance.getEntries()) {  console.log(`    Запись "${entry.name}", тип ${entry.entryType}.    Старт в ${entry.startTime}мс, продолжительность ${entry.duration}мс  `)}
          for (const entry of performance.getEntries()) {
  console.log(`
    Запись "${entry.name}", тип ${entry.entryType}.
    Старт в ${entry.startTime}мс, продолжительность ${entry.duration}мс
  `)
}

        
        
          
        
      

Очищаем список меток и измерений:

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

        
        
          
        
      

Или можем очистить всё сразу:

        
          
          performance.clearResourceTimings()
          performance.clearResourceTimings()

        
        
          
        
      

Как пишется

Скопировано

Создание меток

Скопировано

Метка (mark) — время с начала перехода на страницу до создания метки в миллисекундах. Например, от клика по ссылке или после подтверждения введённого урла в строку поиска.

При создании меток мы можем передать первым аргументом строку - имя метки. В дальнейшем, мы можем обращаться к этому имени для поиска.

        
          
          const markName = 'старт выполнения функции'performance.mark(markName)const entries = performance.getEntriesByName(markName)console.log(entries)
          const markName = 'старт выполнения функции'
performance.mark(markName)

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

        
        
          
        
      

Объект метки содержит значение mark в поле entryType.

Создание измерений

Скопировано

Измерение (measure) - разница во времени между двумя метками. Измерение принимает несколько аргументов:

  1. Имя измерения;
  2. Имя первой метки - необязательный параметр, если не указать, то первой меткой будет время со старта навигации на страницу;
  3. Имя второй метки - необязательный параметр, если не указать, то второй меткой будет вызов performance.now() в момент создания измерения.

В Firefox и некоторых мобильных браузерах вызов метода measure() не возвращает полученное измерение и его нужно запрашивать вручную с помощью getEntriesByName(). Следите за таблицей поддержки.

        
          
          const markOne = 'метка_1'const markTwo = 'метка_2'performance.mark(markOne)performance.mark(markTwo)performance.measure('время со старта навигации на странице')performance.measure('от первой метки до сейчас', markOne)performance.measure('время между двумя метками', markOne, markTwo)const m1 = performance.getEntriesByName('время со старта навигации на странице')[0]const m2 = performance.getEntriesByName('от первой метки до сейчас')[0]const m3 = performance.getEntriesByName('время между двумя метками')[0]console.log({ m1, m2, m3 })
          const markOne = 'метка_1'
const markTwo = 'метка_2'
performance.mark(markOne)
performance.mark(markTwo)

performance.measure('время со старта навигации на странице')
performance.measure('от первой метки до сейчас', markOne)
performance.measure('время между двумя метками', markOne, markTwo)


const m1 = performance.getEntriesByName('время со старта навигации на странице')[0]
const m2 = performance.getEntriesByName('от первой метки до сейчас')[0]
const m3 = performance.getEntriesByName('время между двумя метками')[0]

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

        
        
          
        
      

Способы получения меток и измерений

Скопировано

Получить измерения и метки можно тремя разными способами:

  1. performance.getEntries() - получить список всех меток и измерений, включая записываемые браузером.
  2. performance.getEntriesByType(тип) - получить список из записей заданного типа, например, mark или measure.
  3. performance.getEntriesByName(имя) - получить список из записей с указанным именем.
Подробнее про метки автоматически записываемые браузером

Для улучшения анализа производительности страницы, браузер автоматически записывает некоторые метки:

  1. navigation – события навигации браузера domComplete, loadEventStart, loadEventEnd, redirectCount, domContentLoadedEventStart, domContentLoadedEventEnd, domInteractive, requestStart, responseStart, unloadEventEnd, unloadEventStart.
  2. resource – содержат информацию о загрузке ресурсов сайтом. Например, можно узнать про загрузку стилей или выполнение запросов к API.
  3. paint – информация о рендере страницы, например, время отрисовки первого контента – first-paint, first-contentful-paint.

Любой из способов вернёт массив записей:

        
          
          const mark = performance.mark('старт')const measure = performance.measure('прошло со старта', 'старт')const entries = performance.getEntries()const entriesByName = performance.getEntriesByName('прошло со старта')const onlyMarks = performance.getEntriesByType('mark')console.log(entries)console.log(entriesByName)console.log(onlyMarks)
          const mark = performance.mark('старт')
const measure = performance.measure('прошло со старта', 'старт')
const entries = performance.getEntries()
const entriesByName = performance.getEntriesByName('прошло со старта')
const onlyMarks = performance.getEntriesByType('mark')

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

        
        
          
        
      

Способы очистить записи

Скопировано

Метки и измерения с одинаковыми именами не перезаписывают друг друга. Если одно и то же имя может использоваться в разных частях кода, например, если имена создаются динамически, может быть полезно удалять уже созданные метки перед записью новых с тем же именем. Очистить записанные метки и измерения можно разными методами:

  1. performance.clearMarks(имя_метки) - очистить все записанные метки с переданным именем. Если имя не передать, то удалятся все метки, созданные методом performance.mark().
  2. performance.clearMeasures(имя_измерения) - очищаем все записанные измерения с переданным именем. Если имя не передать, то удалятся все измерения, созданные методом performance.measure().
  3. performance.clearResourceTimings() - очистить все метки, связанные с загрузкой ресурсов браузером.
        
          
          const mark = performance.mark('метка')const measure = performance.measure('измерение')console.log(performance.getEntriesByName('метка').length)// 1performance.clearMarks('метка')performance.clearMeasures('измерение')console.log(performance.getEntriesByName('метка').length)// 0performance.clearResourceTimings()
          const mark = performance.mark('метка')
const measure = performance.measure('измерение')

console.log(performance.getEntriesByName('метка').length)
// 1

performance.clearMarks('метка')
performance.clearMeasures('измерение')

console.log(performance.getEntriesByName('метка').length)
// 0

performance.clearResourceTimings()

        
        
          
        
      

Как понять

Скопировано

Когда нужно проверить скорость работы кода, провести тесты производительности или найти узкие места — на помощь приходит Performance API с его удобными методами и точностью измерений.

Performance API представляет собой реестр записей. Записи бывают разных типов:

  • mark — именная метка времени;
  • measure — измерение. Продолжительность между двумя метками;
  • element — время загрузки элементов;
  • navigation — для записей, связанных с навигацией по сайту;
  • resource — время получение внешних ресурсов (css, запросы API);
  • paint — время первой отрисовки (first paint), либо первой отрисовки контента (first contentful paint);
  • longtask — время работы задачи из LongTasks API;

Тип записи хранится в поле entryType. В ручном режиме мы работаем с метками и измерениями.

На практике

Скопировано

Балдин Кирилл советует

Скопировано

🛠 Удобно анализировать производительность при помощи вкладки «Производительность» (Performance) в инструментах разработчика. Вызовы performance.mark() и performance.measure() будут отображаться в разделе Timings после записи профиля.

Панель отладки производительности с performance.mark
Открыть демо в новой вкладке

🛠 Может появиться желание написать декоратор или функцию-обёртку для performance.mark() и performance.measure() и обернуть в неё всё приложение. Например, так:

        
          
          const withPerformanceMeasure = (markName, functionToAudit) => {  performance.mark(`${markName}-before`)  functionToAudit()  performance.mark(`${markName}-after`)  performance.measure(`${markName}-before`,`${markName}-after`)}// Тело скриптаwithPerformanceMeasure(myApp)
          const withPerformanceMeasure = (markName, functionToAudit) => {
  performance.mark(`${markName}-before`)
  functionToAudit()
  performance.mark(`${markName}-after`)
  performance.measure(`${markName}-before`,`${markName}-after`)
}

// Тело скрипта

withPerformanceMeasure(myApp)

        
        
          
        
      

Этого не стоит делать. Затраты на выполнение функции performance.mark() минимальные, но не нулевые.

Панель Performance Timings для React 17

Владимир Быков советует

Скопировано

🛠 performance полезно использовать для поиска узких мест вашей программы. Рассмотрим пример, когда у нас есть две функции function_1() и function_2() и мы хотим выяснить какая из функций тормозит нашу программу.

Открыть демо в новой вкладке

При измерении видно, что function_1() работает медленнее, а значит для ускорения нужно оптимизировать её.

🛠 Инструменты разработчика позволяют отслеживать производительность программы и другими способами. Например, во вкладке Performance можно записать работу программы и проанализировать время работы отдельных функций или показатели рендеринга. В настройках инструментов разработчика можно включить отображение FPS (количество кадров в секунду) и проверить быстродействие интерфейса.