Перейти к содержанию

🚀 Где задеплоить проект?

Партнёрская ссылка. Дата-центры в РФ, оплата картой.

API измерения производительности

Стабильность: 2 — Стабильная

Модуль реализует подмножество спецификации W3C Web Performance APIs и дополнительные API для измерений производительности, специфичных для Node.js.

В Node.js поддерживаются следующие Web Performance APIs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { performance, PerformanceObserver } from 'node:perf_hooks';

const obs = new PerformanceObserver((items) => {
  console.log(items.getEntries()[0].duration);
  performance.clearMarks();
});
obs.observe({ type: 'measure' });
performance.measure('Start to Now');

performance.mark('A');
doSomeLongRunningProcess(() => {
  performance.measure('A to Now', 'A');

  performance.mark('B');
  performance.measure('A to B', 'A', 'B');
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const { PerformanceObserver, performance } = require('node:perf_hooks');

const obs = new PerformanceObserver((items) => {
  console.log(items.getEntries()[0].duration);
});
obs.observe({ type: 'measure' });
performance.measure('Start to Now');

performance.mark('A');
(async function doSomeLongRunningProcess() {
  await new Promise((r) => setTimeout(r, 5000));
  performance.measure('A to Now', 'A');

  performance.mark('B');
  performance.measure('A to B', 'A', 'B');
})();

perf_hooks.performance

Объект, с помощью которого можно собирать метрики производительности текущего экземпляра Node.js. По смыслу похож на window.performance в браузерах.

performance.clearMarks([name])

Если name не указано, удаляет все объекты PerformanceMark из временной шкалы производительности (Performance Timeline). Если name указано, удаляется только соответствующая метка.

performance.clearMeasures([name])

Если name не указано, удаляет все объекты PerformanceMeasure из временной шкалы производительности. Если name указано, удаляется только соответствующая мера.

performance.clearResourceTimings([name])

Если name не указано, удаляет все объекты PerformanceResourceTiming из шкалы ресурсов (Resource Timeline). Если name указано, удаляется только соответствующий ресурс.

performance.eventLoopUtilization([utilization1[, utilization2]])

  • utilization1 <Object> Результат предыдущего вызова eventLoopUtilization().
  • utilization2 <Object> Результат предыдущего вызова eventLoopUtilization() до момента utilization1.
  • Возвращает: <Object>

Псевдоним perf_hooks.eventLoopUtilization().

Это свойство — расширение Node.js. В веб-браузерах недоступно.

performance.getEntries()

Возвращает список объектов PerformanceEntry в хронологическом порядке относительно performanceEntry.startTime. Если нужны только записи определённых типов или с определёнными именами, см. performance.getEntriesByType() и performance.getEntriesByName().

performance.getEntriesByName(name[, type])

Возвращает список объектов PerformanceEntry в хронологическом порядке относительно performanceEntry.startTime, у которых performanceEntry.name равен name, а при необходимости и performanceEntry.entryType равен type.

performance.getEntriesByType(type)

Возвращает список объектов PerformanceEntry в хронологическом порядке относительно performanceEntry.startTime, у которых performanceEntry.entryType равен type.

performance.mark(name[, options])

  • name <string>
  • options <Object>
    • detail <any> Дополнительные необязательные сведения для метки.
    • startTime <number> Необязательная метка времени для момента метки. По умолчанию: performance.now().

Создаёт новую запись PerformanceMark на временной шкале производительности. PerformanceMark — подкласс PerformanceEntry, у которого performanceEntry.entryType всегда 'mark', а performanceEntry.duration всегда 0. Метки используются, чтобы отмечать важные моменты на временной шкале.

Созданная запись PerformanceMark попадает в глобальную временную шкалу и запрашивается через performance.getEntries, performance.getEntriesByName и performance.getEntriesByType. После наблюдения записи следует вручную очистить из глобальной шкалы вызовом performance.clearMarks.

performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, global, cacheMode, bodyInfo, responseStatus[, deliveryType])

Это свойство — расширение Node.js. В веб-браузерах недоступно.

Создаёт новую запись PerformanceResourceTiming на шкале ресурсов. PerformanceResourceTiming — подкласс PerformanceEntry, у которого performanceEntry.entryType всегда 'resource'. Такие записи отмечают моменты на шкале ресурсов.

Созданная запись попадает в глобальную шкалу ресурсов и запрашивается через performance.getEntries, performance.getEntriesByName и performance.getEntriesByType. После наблюдения записи следует вручную очистить глобальную шкалу вызовом performance.clearResourceTimings.

performance.measure(name[, startMarkOrOptions[, endMark]])

  • name <string>
  • startMarkOrOptions <string> | <Object> Необязательно.
    • detail <any> Дополнительные необязательные сведения для измерения.
    • duration <number> Длительность между началом и концом.
    • end <number> | <string> Метка времени конца или строка с именем ранее записанной метки.
    • start <number> | <string> Метка времени начала или строка с именем ранее записанной метки.
  • endMark <string> Необязательно. Не указывается, если startMarkOrOptionsObject.

Создаёт новую запись PerformanceMeasure на временной шкале производительности. PerformanceMeasure — подкласс PerformanceEntry, у которого performanceEntry.entryType всегда 'measure', а performanceEntry.duration — число миллисекунд между startMark и endMark.

Аргумент startMark может ссылаться на любую существующую PerformanceMark на шкале или на свойства меток времени класса PerformanceNodeTiming. Если метки с указанным именем нет, выбрасывается ошибка.

Необязательный аргумент endMark должен ссылаться на существующую PerformanceMark или на свойства меток времени PerformanceNodeTiming. Если параметр не передан, endMark берётся как performance.now(); если указанное имя не существует — ошибка.

Созданная запись попадает в глобальную временную шкалу и запрашивается через performance.getEntries, performance.getEntriesByName и performance.getEntriesByType. После наблюдения записи следует вручную очистить шкалу вызовом performance.clearMeasures.

performance.nodeTiming

Это свойство — расширение Node.js. В веб-браузерах недоступно.

Экземпляр класса PerformanceNodeTiming с метриками производительности для отдельных этапов работы Node.js.

performance.now()

Возвращает текущую метку времени в миллисекундах с высоким разрешением; 0 соответствует началу текущего процесса node.

performance.setResourceTimingBufferSize(maxSize)

Задаёт размер глобального буфера записей ресурсов (число объектов записей типа "resource").

По умолчанию максимальный размер буфера — 250.

performance.timeOrigin

timeOrigin — метка времени в миллисекундах с высоким разрешением момента запуска текущего процесса node в Unix-времени.

performance.timerify(fn[, options])

  • fn <Function>
  • options <Object>
    • histogram <RecordableHistogram> Гистограмма, созданная через perf_hooks.createHistogram(); записывает длительности выполнения в наносекундах.

Псевдоним perf_hooks.timerify().

Это свойство — расширение Node.js. В веб-браузерах недоступно.

performance.toJSON()

Объект — JSON-представление performance. По смыслу похож на window.performance.toJSON в браузерах.

Событие: 'resourcetimingbufferfull'

Событие 'resourcetimingbufferfull' возникает, когда глобальный буфер записей ресурсов заполнен. Измените размер буфера через performance.setResourceTimingBufferSize() или очистите его через performance.clearResourceTimings() в обработчике, чтобы можно было добавить новые записи на временную шкалу.

Класс: PerformanceEntry

Конструктор этого класса пользователям напрямую недоступен.

performanceEntry.duration

Общее число миллисекунд для этой записи. Для не всех типов записей значение осмысленно.

performanceEntry.entryType

Тип записи производительности. Возможные значения:

  • 'dns' (только Node.js)
  • 'function' (только Node.js)
  • 'gc' (только Node.js)
  • 'http2' (только Node.js)
  • 'http' (только Node.js)
  • 'mark' (доступно в вебе)
  • 'measure' (доступно в вебе)
  • 'net' (только Node.js)
  • 'node' (только Node.js)
  • 'resource' (доступно в вебе)

performanceEntry.name

Имя записи производительности.

performanceEntry.startTime

Метка времени в миллисекундах с высоким разрешением — момент начала записи Performance Entry.

Класс: PerformanceMark

Представляет метки, созданные методом Performance.mark().

performanceMark.detail

Дополнительные сведения, заданные при создании через Performance.mark().

Класс: PerformanceMeasure

Представляет измерения, созданные методом Performance.measure().

Конструктор этого класса пользователям напрямую недоступен.

performanceMeasure.detail

Дополнительные сведения, заданные при создании через Performance.measure().

Класс: PerformanceNodeEntry

Этот класс — расширение Node.js. В веб-браузерах недоступен.

Подробные данные о тайминге Node.js.

Конструктор этого класса пользователям напрямую недоступен.

performanceNodeEntry.detail

Дополнительные сведения, зависящие от entryType.

performanceNodeEntry.flags

Стабильность: 0 — устарело: вместо этого используйте performanceNodeEntry.detail.

Когда performanceEntry.entryType равен 'gc', свойство performance.flags содержит дополнительные сведения об операции сборки мусора. Возможные значения:

  • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_NO
  • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED
  • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_FORCED
  • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING
  • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE
  • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY
  • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE

performanceNodeEntry.kind

Стабильность: 0 — устарело: вместо этого используйте performanceNodeEntry.detail.

Когда performanceEntry.entryType равен 'gc', свойство performance.kind задаёт тип операции сборки мусора. Возможные значения:

  • perf_hooks.constants.NODE_PERFORMANCE_GC_MAJOR
  • perf_hooks.constants.NODE_PERFORMANCE_GC_MINOR
  • perf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTAL
  • perf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB

Сборка мусора ('gc'): подробности

Когда performanceEntry.type равен 'gc', свойство performanceNodeEntry.detail будет Object с двумя полями:

  • kind <number> Одно из:
    • perf_hooks.constants.NODE_PERFORMANCE_GC_MAJOR
    • perf_hooks.constants.NODE_PERFORMANCE_GC_MINOR
    • perf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTAL
    • perf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB
  • flags <number> Одно из:
    • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_NO
    • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED
    • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_FORCED
    • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING
    • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE
    • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY
    • perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE

HTTP ('http'): подробности

Когда performanceEntry.type равен 'http', свойство performanceNodeEntry.detail — это Object с дополнительной информацией.

Если performanceEntry.name равен HttpClient, в detail будут поля req, res: reqObject с method, url, headers; resObject с statusCode, statusMessage, headers.

Если performanceEntry.name равен HttpRequest, структура такая же: req, res с теми же полями.

Это может увеличить расход памяти; используйте только для диагностики, не оставляйте включённым в production по умолчанию.

HTTP/2 ('http2'): подробности

Когда performanceEntry.type равен 'http2', performanceNodeEntry.detailObject с дополнительными сведениями о производительности.

Если performanceEntry.name равен Http2Stream, в detail будут поля:

  • bytesRead <number> Число байт кадров DATA, полученных для этого Http2Stream.
  • bytesWritten <number> Число байт кадров DATA, отправленных для этого Http2Stream.
  • id <number> Идентификатор связанного Http2Stream
  • timeToFirstByte <number> Миллисекунды между PerformanceEntry.startTime и приёмом первого кадра DATA.
  • timeToFirstByteSent <number> Миллисекунды между PerformanceEntry.startTime и отправкой первого кадра DATA.
  • timeToFirstHeader <number> Миллисекунды между PerformanceEntry.startTime и приёмом первого заголовка.

Если performanceEntry.name равен Http2Session, в detail будут поля:

  • bytesRead <number> Число байт, полученных для этого Http2Session.
  • bytesWritten <number> Число байт, отправленных для этого Http2Session.
  • framesReceived <number> Число кадров HTTP/2, принятых Http2Session.
  • framesSent <number> Число кадров HTTP/2, отправленных Http2Session.
  • maxConcurrentStreams <number> Максимум одновременно открытых потоков за время жизни Http2Session.
  • pingRTT <number> Миллисекунды от отправки кадра PING до подтверждения; есть только если PING отправлялся на Http2Session.
  • streamAverageDuration <number> Средняя длительность (мс) по всем Http2Stream.
  • streamCount <number> Число обработанных Http2Stream в Http2Session.
  • type <string> 'server' или 'client' — тип Http2Session.

Timerify ('function'): подробности

Когда performanceEntry.type равен 'function', performanceNodeEntry.detail — это Array с аргументами измеряемой функции.

Сеть ('net'): подробности

Когда performanceEntry.type равен 'net', performanceNodeEntry.detailObject с дополнительной информацией.

Если performanceEntry.name равен connect, в detail будут host, port.

DNS ('dns'): подробности

Когда performanceEntry.type равен 'dns', performanceNodeEntry.detailObject с дополнительной информацией.

Если performanceEntry.name равен lookup, в detail будут hostname, family, hints, verbatim, addresses.

Если performanceEntry.name равен lookupService, в detail будут host, port, hostname, service.

Если performanceEntry.name равен queryxxx или getHostByAddr, в detail будут host, ttl, result; значение result совпадает с результатом queryxxx или getHostByAddr.

Класс: PerformanceNodeTiming

Это свойство — расширение Node.js. В веб-браузерах недоступно.

Сведения о тайминге самого Node.js. Конструктор класса пользователям недоступен.

performanceNodeTiming.bootstrapComplete

Метка времени в миллисекундах с высоким разрешением — момент завершения начальной загрузки процесса Node.js. Если загрузка ещё не завершена, значение −1.

performanceNodeTiming.environment

Метка времени в миллисекундах с высоким разрешением — момент инициализации среды Node.js.

performanceNodeTiming.idleTime

Метка времени в миллисекундах с высоким разрешением — время простоя цикла событий в провайдере событий (например epoll_wait). Загрузка CPU не учитывается. Если цикл событий ещё не запущен (например, в первом тике основного скрипта), значение 0.

performanceNodeTiming.loopExit

Метка времени в миллисекундах с высоким разрешением — момент выхода из цикла событий Node.js. Если цикл ещё не завершён, значение −1. Значение, отличное от −1, возможно только в обработчике события 'exit'.

performanceNodeTiming.loopStart

Метка времени в миллисекундах с высоким разрешением — момент запуска цикла событий Node.js. Если цикл ещё не начался (например, в первом тике основного скрипта), значение −1.

performanceNodeTiming.nodeStart

Метка времени в миллисекундах с высоким разрешением — момент инициализации процесса Node.js.

performanceNodeTiming.uvMetricsInfo

  • Возвращает: <Object>
    • loopCount <number> Число итераций цикла событий.
    • events <number> Число событий, обработанных обработчиком.
    • eventsWaiting <number> Число событий, ожидавших обработки при вызове провайдера.

Обёртка над функцией uv_metrics_info; возвращает текущие метрики цикла событий.

Рекомендуется читать это свойство внутри функции, запланированной через setImmediate, чтобы не собирать метрики до завершения всех операций, запланированных в текущей итерации цикла.

1
2
3
4
5
const { performance } = require('node:perf_hooks');

setImmediate(() => {
  console.log(performance.nodeTiming.uvMetricsInfo);
});
1
2
3
4
5
import { performance } from 'node:perf_hooks';

setImmediate(() => {
  console.log(performance.nodeTiming.uvMetricsInfo);
});

performanceNodeTiming.v8Start

Метка времени в миллисекундах с высоким разрешением — момент инициализации платформы V8.

Класс: PerformanceResourceTiming

Подробные сетевые метрики времени загрузки ресурсов приложения.

Конструктор этого класса пользователям напрямую недоступен.

performanceResourceTiming.workerStart

Метка времени в миллисекундах с высоким разрешением — момент непосредственно перед отправкой запроса fetch. Если ресурс не перехвачен worker, свойство всегда 0.

performanceResourceTiming.redirectStart

Метка времени в миллисекундах с высоким разрешением — начало выборки, инициировавшей редирект.

performanceResourceTiming.redirectEnd

Метка времени в миллисекундах с высоким разрешением — сразу после получения последнего байта ответа последнего редиректа.

performanceResourceTiming.fetchStart

Метка времени в миллисекундах с высоким разрешением — непосредственно перед началом выборки ресурса в Node.js.

performanceResourceTiming.domainLookupStart

Метка времени в миллисекундах с высоким разрешением — непосредственно перед началом DNS-запроса для ресурса.

performanceResourceTiming.domainLookupEnd

Метка времени в миллисекундах с высоким разрешением — сразу после завершения DNS-поиска для ресурса.

performanceResourceTiming.connectStart

Метка времени в миллисекундах с высоким разрешением — непосредственно перед установлением соединения с сервером для получения ресурса.

performanceResourceTiming.connectEnd

Метка времени в миллисекундах с высоким разрешением — сразу после установления соединения с сервером для получения ресурса.

performanceResourceTiming.secureConnectionStart

Метка времени в миллисекундах с высоким разрешением — непосредственно перед началом рукопожатия для защиты текущего соединения.

performanceResourceTiming.requestStart

Метка времени в миллисекундах с высоким разрешением — непосредственно перед получением первого байта ответа от сервера.

performanceResourceTiming.responseEnd

Метка времени в миллисекундах с высоким разрешением — сразу после получения последнего байта ресурса или непосредственно перед закрытием транспортного соединения, в зависимости от того, что наступит раньше.

performanceResourceTiming.transferSize

Число — размер (в октетах) полученного ресурса: поля заголовка ответа плюс тело полезной нагрузки.

performanceResourceTiming.encodedBodySize

Число — размер (в октетах) тела полезной нагрузки, полученного при выборке (HTTP или кэш), до снятия кодирований содержимого.

performanceResourceTiming.decodedBodySize

Число — размер (в октетах) тела сообщения, полученного при выборке (HTTP или кэш), после снятия кодирований содержимого.

performanceResourceTiming.toJSON()

Возвращает объект — JSON-представление PerformanceResourceTiming.

Класс: PerformanceObserver

PerformanceObserver.supportedEntryTypes

Возвращает поддерживаемые типы.

new PerformanceObserver(callback)

Объекты PerformanceObserver уведомляют о появлении новых экземпляров PerformanceEntry на временной шкале производительности.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import { performance, PerformanceObserver } from 'node:perf_hooks';

const obs = new PerformanceObserver((list, observer) => {
  console.log(list.getEntries());

  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ entryTypes: ['mark'], buffered: true });

performance.mark('test');
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const {
  performance,
  PerformanceObserver,
} = require('node:perf_hooks');

const obs = new PerformanceObserver((list, observer) => {
  console.log(list.getEntries());

  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ entryTypes: ['mark'], buffered: true });

performance.mark('test');

Так как экземпляры PerformanceObserver добавляют собственные накладные расходы, их не следует оставлять подписанными бесконечно. Отключайте наблюдателей, как только они не нужны.

Колбэк вызывается, когда PerformanceObserver получает уведомление о новых экземплярах PerformanceEntry. В колбэк передаются экземпляр PerformanceObserverEntryList и ссылка на PerformanceObserver.

performanceObserver.disconnect()

Отключает экземпляр PerformanceObserver от всех уведомлений.

performanceObserver.observe(options)

  • options <Object>
    • type <string> Один тип PerformanceEntry. Не указывайте, если уже задан entryTypes.
    • entryTypes <string[]> Массив строк с типами PerformanceEntry, которые интересуют наблюдателя. Если не указан — ошибка.
    • buffered <boolean> Если true, колбэк вызывается со списком глобальных буферизованных записей PerformanceEntry. Если false — только записи, созданные после момента времени. По умолчанию: false.

Подписывает PerformanceObserver на уведомления о новых PerformanceEntry, выбранных через options.entryTypes или options.type:

1
2
3
4
5
6
7
8
9
import { performance, PerformanceObserver } from 'node:perf_hooks';

const obs = new PerformanceObserver((list, observer) => {
  // Called once asynchronously. `list` contains three items.
});
obs.observe({ type: 'mark' });

for (let n = 0; n < 3; n++)
  performance.mark(`test${n}`);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const {
  performance,
  PerformanceObserver,
} = require('node:perf_hooks');

const obs = new PerformanceObserver((list, observer) => {
  // Called once asynchronously. `list` contains three items.
});
obs.observe({ type: 'mark' });

for (let n = 0; n < 3; n++)
  performance.mark(`test${n}`);

performanceObserver.takeRecords()

  • Возвращает: <PerformanceEntry[]> Текущий список записей в наблюдателе; после вызова список очищается.

Класс: PerformanceObserverEntryList

Класс PerformanceObserverEntryList даёт доступ к экземплярам PerformanceEntry, переданным в PerformanceObserver. Конструктор класса пользователям недоступен.

performanceObserverEntryList.getEntries()

Возвращает список объектов PerformanceEntry в хронологическом порядке относительно performanceEntry.startTime.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { performance, PerformanceObserver } from 'node:perf_hooks';

const obs = new PerformanceObserver((perfObserverList, observer) => {
  console.log(perfObserverList.getEntries());
  /**
   * [
   *   PerformanceEntry {
   *     name: 'test',
   *     entryType: 'mark',
   *     startTime: 81.465639,
   *     duration: 0,
   *     detail: null
   *   },
   *   PerformanceEntry {
   *     name: 'meow',
   *     entryType: 'mark',
   *     startTime: 81.860064,
   *     duration: 0,
   *     detail: null
   *   }
   * ]
   */

  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ type: 'mark' });

performance.mark('test');
performance.mark('meow');
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const {
  performance,
  PerformanceObserver,
} = require('node:perf_hooks');

const obs = new PerformanceObserver((perfObserverList, observer) => {
  console.log(perfObserverList.getEntries());
  /**
   * [
   *   PerformanceEntry {
   *     name: 'test',
   *     entryType: 'mark',
   *     startTime: 81.465639,
   *     duration: 0,
   *     detail: null
   *   },
   *   PerformanceEntry {
   *     name: 'meow',
   *     entryType: 'mark',
   *     startTime: 81.860064,
   *     duration: 0,
   *     detail: null
   *   }
   * ]
   */

  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ type: 'mark' });

performance.mark('test');
performance.mark('meow');

performanceObserverEntryList.getEntriesByName(name[, type])

Возвращает список объектов PerformanceEntry в хронологическом порядке относительно performanceEntry.startTime, у которых performanceEntry.name равен name, а при необходимости и performanceEntry.entryType равен type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { performance, PerformanceObserver } from 'node:perf_hooks';

const obs = new PerformanceObserver((perfObserverList, observer) => {
  console.log(perfObserverList.getEntriesByName('meow'));
  /**
   * [
   *   PerformanceEntry {
   *     name: 'meow',
   *     entryType: 'mark',
   *     startTime: 98.545991,
   *     duration: 0,
   *     detail: null
   *   }
   * ]
   */
  console.log(perfObserverList.getEntriesByName('nope')); // []

  console.log(perfObserverList.getEntriesByName('test', 'mark'));
  /**
   * [
   *   PerformanceEntry {
   *     name: 'test',
   *     entryType: 'mark',
   *     startTime: 63.518931,
   *     duration: 0,
   *     detail: null
   *   }
   * ]
   */
  console.log(perfObserverList.getEntriesByName('test', 'measure')); // []

  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ entryTypes: ['mark', 'measure'] });

performance.mark('test');
performance.mark('meow');
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const {
  performance,
  PerformanceObserver,
} = require('node:perf_hooks');

const obs = new PerformanceObserver((perfObserverList, observer) => {
  console.log(perfObserverList.getEntriesByName('meow'));
  /**
   * [
   *   PerformanceEntry {
   *     name: 'meow',
   *     entryType: 'mark',
   *     startTime: 98.545991,
   *     duration: 0,
   *     detail: null
   *   }
   * ]
   */
  console.log(perfObserverList.getEntriesByName('nope')); // []

  console.log(perfObserverList.getEntriesByName('test', 'mark'));
  /**
   * [
   *   PerformanceEntry {
   *     name: 'test',
   *     entryType: 'mark',
   *     startTime: 63.518931,
   *     duration: 0,
   *     detail: null
   *   }
   * ]
   */
  console.log(perfObserverList.getEntriesByName('test', 'measure')); // []

  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ entryTypes: ['mark', 'measure'] });

performance.mark('test');
performance.mark('meow');

performanceObserverEntryList.getEntriesByType(type)

Возвращает список объектов PerformanceEntry в хронологическом порядке относительно performanceEntry.startTime, у которых performanceEntry.entryType равен type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { performance, PerformanceObserver } from 'node:perf_hooks';

const obs = new PerformanceObserver((perfObserverList, observer) => {
  console.log(perfObserverList.getEntriesByType('mark'));
  /**
   * [
   *   PerformanceEntry {
   *     name: 'test',
   *     entryType: 'mark',
   *     startTime: 55.897834,
   *     duration: 0,
   *     detail: null
   *   },
   *   PerformanceEntry {
   *     name: 'meow',
   *     entryType: 'mark',
   *     startTime: 56.350146,
   *     duration: 0,
   *     detail: null
   *   }
   * ]
   */
  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ type: 'mark' });

performance.mark('test');
performance.mark('meow');
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const {
  performance,
  PerformanceObserver,
} = require('node:perf_hooks');

const obs = new PerformanceObserver((perfObserverList, observer) => {
  console.log(perfObserverList.getEntriesByType('mark'));
  /**
   * [
   *   PerformanceEntry {
   *     name: 'test',
   *     entryType: 'mark',
   *     startTime: 55.897834,
   *     duration: 0,
   *     detail: null
   *   },
   *   PerformanceEntry {
   *     name: 'meow',
   *     entryType: 'mark',
   *     startTime: 56.350146,
   *     duration: 0,
   *     detail: null
   *   }
   * ]
   */
  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ type: 'mark' });

performance.mark('test');
performance.mark('meow');

perf_hooks.createHistogram([options])

  • options <Object>
    • lowest <number> | <bigint> Минимальное различимое значение; целое число > 0. По умолчанию: 1.
    • highest <number> | <bigint> Максимальная записываемая величина; целое ≥ 2×lowest. По умолчанию: Number.MAX_SAFE_INTEGER.
    • figures <number> Число значащих цифр; от 1 до 5. По умолчанию: 3.
  • Возвращает: <RecordableHistogram>

Возвращает RecordableHistogram.

perf_hooks.eventLoopUtilization([utilization1[, utilization2]])

  • utilization1 <Object> Результат предыдущего вызова eventLoopUtilization().
  • utilization2 <Object> Результат предыдущего вызова eventLoopUtilization() до момента utilization1.
  • Возвращает: <Object>

Функция eventLoopUtilization() возвращает объект с суммарной длительностью времени, когда цикл событий был и простаивал, и активен, в виде таймера с высоким разрешением в миллисекундах. Поле utilization — рассчитанная утилизация цикла событий (ELU).

Если на главном потоке загрузка ещё не завершена, свойства равны 0. ELU сразу доступен в Worker threads, так как загрузка происходит внутри цикла событий.

Оба параметра utilization1 и utilization2 необязательны.

Если передан utilization1, вычисляется и возвращается дельта между текущими active и idle и соответствующее utilization (аналогично process.hrtime()).

Если переданы оба — utilization1 и utilization2, дельта считается между ними. Это удобно, потому что для ELU недостаточно простого вычитания, в отличие от process.hrtime().

ELU похожа на загрузку CPU, но измеряет только статистику цикла событий, а не CPU. Это доля времени, которую цикл провёл вне провайдера событий (например epoll_wait). Иное время простоя CPU не учитывается. Ниже — пример: в основном простаивающий процесс может иметь высокий ELU.

1
2
3
4
5
6
7
8
import { eventLoopUtilization } from 'node:perf_hooks';
import { spawnSync } from 'node:child_process';

setImmediate(() => {
  const elu = eventLoopUtilization();
  spawnSync('sleep', ['5']);
  console.log(eventLoopUtilization(elu).utilization);
});
1
2
3
4
5
6
7
8
9
'use strict';
const { eventLoopUtilization } = require('node:perf_hooks');
const { spawnSync } = require('node:child_process');

setImmediate(() => {
  const elu = eventLoopUtilization();
  spawnSync('sleep', ['5']);
  console.log(eventLoopUtilization(elu).utilization);
});

Хотя при выполнении этого сценария CPU в основном простаивает, utilization равен 1, потому что child_process.spawnSync() блокирует цикл событий.

Передача произвольного объекта вместо результата предыдущего вызова eventLoopUtilization() даёт неопределённое поведение; возвращаемые значения не гарантируют корректное состояние цикла событий.

perf_hooks.monitorEventLoopDelay([options])

  • options <Object>
    • resolution <number> Период выборки в миллисекундах; должен быть > 0. По умолчанию: 10.
  • Возвращает: <IntervalHistogram>

This property is an extension by Node.js. It is not available in Web browsers.

Создаёт объект IntervalHistogram, который снимает и сообщает задержку цикла событий во времени; задержки в наносекундах.

Таймер подходит для оценки задержки цикла, потому что выполнение таймеров привязано к жизненному циклу цикла событий libuv: задержка в цикле задерживает таймер — именно это и предназначено измерять этому API.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { monitorEventLoopDelay } from 'node:perf_hooks';

const h = monitorEventLoopDelay({ resolution: 20 });
h.enable();
// Do something.
h.disable();
console.log(h.min);
console.log(h.max);
console.log(h.mean);
console.log(h.stddev);
console.log(h.percentiles);
console.log(h.percentile(50));
console.log(h.percentile(99));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const { monitorEventLoopDelay } = require('node:perf_hooks');
const h = monitorEventLoopDelay({ resolution: 20 });
h.enable();
// Do something.
h.disable();
console.log(h.min);
console.log(h.max);
console.log(h.mean);
console.log(h.stddev);
console.log(h.percentiles);
console.log(h.percentile(50));
console.log(h.percentile(99));

perf_hooks.timerify(fn[, options])

  • fn <Function>
  • options <Object>
    • histogram <RecordableHistogram> Гистограмма, созданная через perf_hooks.createHistogram(); записывает длительности выполнения в наносекундах.

Это свойство — расширение Node.js. В веб-браузерах недоступно.

Оборачивает функцию в новую, измеряющую время выполнения обёрнутой функции. Чтобы получить детали времени, нужно подписать PerformanceObserver на тип события 'function'.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import { timerify, performance, PerformanceObserver } from 'node:perf_hooks';

function someFunction() {
  console.log('hello world');
}

const wrapped = timerify(someFunction);

const obs = new PerformanceObserver((list) => {
  console.log(list.getEntries()[0].duration);

  performance.clearMarks();
  performance.clearMeasures();
  obs.disconnect();
});
obs.observe({ entryTypes: ['function'] });

// A performance timeline entry will be created
wrapped();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const {
  timerify,
  performance,
  PerformanceObserver,
} = require('node:perf_hooks');

function someFunction() {
  console.log('hello world');
}

const wrapped = timerify(someFunction);

const obs = new PerformanceObserver((list) => {
  console.log(list.getEntries()[0].duration);

  performance.clearMarks();
  performance.clearMeasures();
  obs.disconnect();
});
obs.observe({ entryTypes: ['function'] });

// A performance timeline entry will be created
wrapped();

Если обёрнутая функция возвращает промис, к нему добавляется обработчик finally; длительность сообщается после его вызова.

Класс: Histogram

histogram.count

Число образцов, записанных в гистограмму.

histogram.countBigInt

Число образцов, записанных в гистограмму.

histogram.exceeds

Сколько раз задержка цикла событий превысила порог максимальной задержки 1 час.

histogram.exceedsBigInt

Сколько раз задержка цикла событий превысила порог максимальной задержки 1 час.

histogram.max

Максимальная зафиксированная задержка цикла событий.

histogram.maxBigInt

Максимальная зафиксированная задержка цикла событий.

histogram.mean

Среднее зафиксированных задержек цикла событий.

histogram.min

Минимальная зафиксированная задержка цикла событий.

histogram.minBigInt

Минимальная зафиксированная задержка цикла событий.

histogram.percentile(percentile)

  • percentile <number> Перцентиль в диапазоне (0, 100].
  • Возвращает: <number>

Возвращает значение для заданного перцентиля.

histogram.percentileBigInt(percentile)

  • percentile <number> Перцентиль в диапазоне (0, 100].
  • Возвращает: <bigint>

Возвращает значение для заданного перцентиля.

histogram.percentiles

Возвращает объект Map с накопленным распределением по перцентилям.

histogram.percentilesBigInt

Возвращает объект Map с накопленным распределением по перцентилям.

histogram.reset()

Сбрасывает накопленные данные гистограммы.

histogram.stddev

Стандартное отклонение зафиксированных задержек цикла событий.

Класс: IntervalHistogram extends Histogram

Histogram, периодически обновляемый с заданным интервалом.

histogram.disable()

Отключает таймер обновления. Возвращает true, если таймер остановлен, false, если уже был остановлен.

histogram.enable()

Включает таймер обновления. Возвращает true, если таймер запущен, false, если уже был запущен.

histogram[Symbol.dispose]()

Отключает таймер обновления при освобождении гистограммы.

1
2
3
4
5
6
const { monitorEventLoopDelay } = require('node:perf_hooks');
{
  using hist = monitorEventLoopDelay({ resolution: 20 });
  hist.enable();
  // The histogram will be disabled when the block is exited.
}

Клонирование IntervalHistogram

Экземпляры IntervalHistogram можно клонировать через MessagePort. На приёмной стороне гистограмма клонируется как обычный Histogram без методов enable() и disable().

Класс: RecordableHistogram extends Histogram

histogram.add(other)

Добавляет значения из other в эту гистограмму.

histogram.record(val)

  • val <number> | <bigint> Величина для записи в гистограмму.

histogram.recordDelta()

Вычисляет время (в наносекундах) с предыдущего вызова recordDelta() и записывает его в гистограмму.

Примеры

Измерение длительности асинхронных операций

В примере используются Async Hooks и Performance API, чтобы измерить фактическую длительность операции Timeout (включая время выполнения колбэка).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { createHook } from 'node:async_hooks';
import { performance, PerformanceObserver } from 'node:perf_hooks';

const set = new Set();
const hook = createHook({
  init(id, type) {
    if (type === 'Timeout') {
      performance.mark(`Timeout-${id}-Init`);
      set.add(id);
    }
  },
  destroy(id) {
    if (set.has(id)) {
      set.delete(id);
      performance.mark(`Timeout-${id}-Destroy`);
      performance.measure(`Timeout-${id}`,
                          `Timeout-${id}-Init`,
                          `Timeout-${id}-Destroy`);
    }
  },
});
hook.enable();

const obs = new PerformanceObserver((list, observer) => {
  console.log(list.getEntries()[0]);
  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ entryTypes: ['measure'], buffered: true });

setTimeout(() => {}, 1000);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
'use strict';
const async_hooks = require('node:async_hooks');
const {
  performance,
  PerformanceObserver,
} = require('node:perf_hooks');

const set = new Set();
const hook = async_hooks.createHook({
  init(id, type) {
    if (type === 'Timeout') {
      performance.mark(`Timeout-${id}-Init`);
      set.add(id);
    }
  },
  destroy(id) {
    if (set.has(id)) {
      set.delete(id);
      performance.mark(`Timeout-${id}-Destroy`);
      performance.measure(`Timeout-${id}`,
                          `Timeout-${id}-Init`,
                          `Timeout-${id}-Destroy`);
    }
  },
});
hook.enable();

const obs = new PerformanceObserver((list, observer) => {
  console.log(list.getEntries()[0]);
  performance.clearMarks();
  performance.clearMeasures();
  observer.disconnect();
});
obs.observe({ entryTypes: ['measure'] });

setTimeout(() => {}, 1000);

Сколько времени уходит на загрузку зависимостей

Пример измеряет длительность операций require() при загрузке зависимостей:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import { performance, PerformanceObserver } from 'node:perf_hooks';

// Activate the observer
const obs = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  entries.forEach((entry) => {
    console.log(`import('${entry[0]}')`, entry.duration);
  });
  performance.clearMarks();
  performance.clearMeasures();
  obs.disconnect();
});
obs.observe({ entryTypes: ['function'], buffered: true });

const timedImport = performance.timerify(async (module) => {
  return await import(module);
});

await timedImport('some-module');
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
'use strict';
const {
  performance,
  PerformanceObserver,
} = require('node:perf_hooks');
const mod = require('node:module');

// Monkey patch the require function
mod.Module.prototype.require =
  performance.timerify(mod.Module.prototype.require);
require = performance.timerify(require);

// Activate the observer
const obs = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  entries.forEach((entry) => {
    console.log(`require('${entry[0]}')`, entry.duration);
  });
  performance.clearMarks();
  performance.clearMeasures();
  obs.disconnect();
});
obs.observe({ entryTypes: ['function'] });

require('some-module');

Длительность одного HTTP round-trip

Пример показывает время для HTTP-клиента (OutgoingMessage) и HTTP-запроса (IncomingMessage): для клиента — интервал от начала запроса до получения ответа; для запроса — от получения запроса до отправки ответа:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import { PerformanceObserver } from 'node:perf_hooks';
import { createServer, get } from 'node:http';

const obs = new PerformanceObserver((items) => {
  items.getEntries().forEach((item) => {
    console.log(item);
  });
});

obs.observe({ entryTypes: ['http'] });

const PORT = 8080;

createServer((req, res) => {
  res.end('ok');
}).listen(PORT, () => {
  get(`http://127.0.0.1:${PORT}`);
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
'use strict';
const { PerformanceObserver } = require('node:perf_hooks');
const http = require('node:http');

const obs = new PerformanceObserver((items) => {
  items.getEntries().forEach((item) => {
    console.log(item);
  });
});

obs.observe({ entryTypes: ['http'] });

const PORT = 8080;

http.createServer((req, res) => {
  res.end('ok');
}).listen(PORT, () => {
  http.get(`http://127.0.0.1:${PORT}`);
});

Измерение времени net.connect (только для TCP) при успешном подключении

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { PerformanceObserver } from 'node:perf_hooks';
import { connect, createServer } from 'node:net';

const obs = new PerformanceObserver((items) => {
  items.getEntries().forEach((item) => {
    console.log(item);
  });
});
obs.observe({ entryTypes: ['net'] });
const PORT = 8080;
createServer((socket) => {
  socket.destroy();
}).listen(PORT, () => {
  connect(PORT);
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
'use strict';
const { PerformanceObserver } = require('node:perf_hooks');
const net = require('node:net');
const obs = new PerformanceObserver((items) => {
  items.getEntries().forEach((item) => {
    console.log(item);
  });
});
obs.observe({ entryTypes: ['net'] });
const PORT = 8080;
net.createServer((socket) => {
  socket.destroy();
}).listen(PORT, () => {
  net.connect(PORT);
});

Измерение времени DNS при успешном запросе

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import { PerformanceObserver } from 'node:perf_hooks';
import { lookup, promises } from 'node:dns';

const obs = new PerformanceObserver((items) => {
  items.getEntries().forEach((item) => {
    console.log(item);
  });
});
obs.observe({ entryTypes: ['dns'] });
lookup('localhost', () => {});
promises.resolve('localhost');
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
'use strict';
const { PerformanceObserver } = require('node:perf_hooks');
const dns = require('node:dns');
const obs = new PerformanceObserver((items) => {
  items.getEntries().forEach((item) => {
    console.log(item);
  });
});
obs.observe({ entryTypes: ['dns'] });
dns.lookup('localhost', () => {});
dns.promises.resolve('localhost');

Комментарии