API измерения производительности
Исходный код: lib/perf_hooks.js
Этот модуль предоставляет реализацию подмножества API производительности веб-страниц W3C Web Performance APIs, а также дополнительные API для измерений производительности, специфичных для Node.js.
Node.js поддерживает следующие API производительности веб-страниц:
const { PerformanceObserver, performance } = require('perf_hooks');
const obs = new PerformanceObserver((items) => {
console.log(items.getEntries()[0].duration);
performance.clearMarks();
});
obs.observe({ entryTypes: ['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');
}); perf_hooks.performance
Объект, который можно использовать для сбора метрик производительности текущего экземпляра Node.js. Он похож на window.performance в браузерах.
performance.clearMarks([name])
-
name<строка>
Если name не указан, удаляет все объекты PerformanceMark из временной шкалы производительности. Если name указан, удаляет только метку с указанным именем.
performance.eventLoopUtilization([utilization1[, utilization2]])
-
utilization1<объект> Результат предыдущего вызоваeventLoopUtilization(). -
utilization2<объект> Результат предыдущего вызоваeventLoopUtilization()доutilization1. - Возвращает <объект>
Метод eventLoopUtilization() возвращает объект, содержащий суммарную продолжительность времени, в течение которого цикл событий был как простаивал, так и активен, как таймер с высокой точностью в миллисекундах. Значение utilization — это вычисленная загрузка цикла событий (ELU).
Если запуск на основном потоке еще не завершен, свойства имеют значение 0. ELU немедленно доступна в потоках-работниках, так как запуск происходит внутри цикла событий.
И utilization1, и utilization2 — необязательные параметры.
Если utilization1 передан, тогда вычисляется разница между текущим вызовом active и idle временем, а также соответствующее значение utilization (подобно process.hrtime()).
Если utilization1 и utilization2 оба переданы, тогда вычисляется разница между двумя аргументами. Это удобный вариант, потому что, в отличие от process.hrtime(), вычисление ELU более сложно, чем простое вычитание.
ELU похожа на загрузку ЦП, но она измеряет только статистику цикла событий, а не использование ЦП. Она представляет собой процент времени, затраченного циклом событий вне поставщика событий цикла событий (например, epoll_wait). Другое время простоя ЦП не учитывается. Следующий пример показывает, как у процесса, в основном простаивающего, будет высокая ELU.
'use strict';
const { eventLoopUtilization } = require('perf_hooks').performance;
const { spawnSync } = require('child_process');
setImmediate(() => {
const elu = eventLoopUtilization();
spawnSync('sleep', ['5']);
console.log(eventLoopUtilization(elu).utilization);
}); Хотя ЦП в основном простаивает во время выполнения этого скрипта, значение utilization равно 1. Это связано с тем, что вызов child_process.spawnSync() блокирует цикл событий, не позволяя ему продолжить работу.
Передача пользовательского объекта вместо результата предыдущего вызова eventLoopUtilization() приведет к неопределенному поведению. Возвращаемые значения не гарантируют отражения правильного состояния цикла событий.
performance.mark([name])
-
name<строка>
Создает новую запись PerformanceMark во временной шкале производительности. PerformanceMark — подкласс PerformanceEntry, чьё значение performanceEntry.entryType всегда 'mark', а чьё значение performanceEntry.duration всегда 0. Метки производительности используются для маркирования конкретных значительных моментов во временной шкале производительности.
performance.measure(name[, startMark[, endMark]])
Создает новую запись PerformanceMeasure во временной шкале производительности. PerformanceMeasure — подкласс PerformanceEntry, чьё значение performanceEntry.entryType всегда 'measure', а чьё значение performanceEntry.duration измеряет количество миллисекунд, прошедших с момента startMark и endMark.
Аргумент startMark может идентифицировать любую существующую PerformanceMark во временной шкале производительности или может идентифицировать любое из свойств временных меток, предоставляемых классом PerformanceNodeTiming. Если указанная startMark не существует, то startMark устанавливается в значение timeOrigin по умолчанию.
Необязательный аргумент endMark должен идентифицировать любую существующую PerformanceMark во временной шкале производительности или любое из свойств временных меток, предоставляемых классом PerformanceNodeTiming. endMark будет иметь значение performance.now(), если параметр не передан; в противном случае, если указанная endMark не существует, будет выброшено исключение.
performance.nodeTiming
Это свойство является расширением Node.js. Оно недоступно в веб-браузерах.
Экземпляр класса PerformanceNodeTiming, который предоставляет метрики производительности для конкретных вех Node.js.
performance.now()
- Возвращает: <число>
Возвращает текущую высокую временную метку миллисекунд, где 0 представляет начало текущего node процесса.
performance.timeOrigin
Значение timeOrigin указывает на временную метку миллисекунд с высокой точностью, в момент начала текущего node процесса, измеренную в временной шкале Unix.
performance.timerify(fn)
-
fn<функция>
Это свойство является расширением Node.js. Оно недоступно в веб-браузерах.
Оборачивает функцию новой функцией, которая измеряет время выполнения обернутой функции. PerformanceObserver должно быть подписано на тип события 'function', чтобы получить доступ к деталям измерения времени.
const {
performance,
PerformanceObserver
} = require('perf_hooks');
function someFunction() {
console.log('hello world');
}
const wrapped = performance.timerify(someFunction);
const obs = new PerformanceObserver((list) => {
console.log(list.getEntries()[0].duration);
obs.disconnect();
});
obs.observe({ entryTypes: ['function'] });
// A performance timeline entry will be created
wrapped(); Класс: PerformanceEntry
performanceEntry.duration
Общее количество миллисекунд, прошедших для этой записи. Это значение не будет иметь смысла для всех типов записей Performance.
performanceEntry.entryType
Тип записи производительности. Может быть одним из:
-
'node'(только Node.js) -
'mark'(доступно в браузере) -
'measure'(доступно в браузере) -
'gc'(только Node.js) -
'function'(только Node.js) -
'http2'(только Node.js) -
'http'(только Node.js)
performanceEntry.flags
Это свойство расширение Node.js. Оно недоступно в веб-браузерах.
Когда performanceEntry.entryType равно 'gc', свойство performance.flags содержит дополнительную информацию об операции сборки мусора. Значение может быть одним из:
perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_NOperf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINEDperf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_FORCEDperf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSINGperf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGEperf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORYperf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE
performanceEntry.name
Имя записи производительности.
performanceEntry.kind
Это свойство расширение Node.js. Оно недоступно в веб-браузерах.
Когда performanceEntry.entryType равно 'gc', свойство performance.kind идентифицирует тип операции сборки мусора, которая произошла. Значение может быть одним из:
perf_hooks.constants.NODE_PERFORMANCE_GC_MAJORperf_hooks.constants.NODE_PERFORMANCE_GC_MINORperf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTALperf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB
performanceEntry.startTime
Отметка времени в миллисекундах с высоким разрешением, обозначающая начальное время записи производительности.
Класс: PerformanceNodeTiming
- Расширяет: <PerformanceEntry>
Это свойство расширение Node.js. Оно недоступно в веб-браузерах.
Предоставляет сведения о времени выполнения самого Node.js. Конструктор этого класса не доступен пользователям.
performanceNodeTiming.bootstrapComplete
Отметка времени в миллисекундах с высоким разрешением, в которой процесс Node.js завершил загрузку. Если загрузка ещё не завершена, свойство имеет значение -1.
performanceNodeTiming.environment
Отметка времени в миллисекундах с высоким разрешением, в которой была инициализирована среда Node.js.
performanceNodeTiming.idleTime
Отметка времени в миллисекундах с высоким разрешением, указывающая, сколько времени цикл событий был неактивен в поставщике событий цикла событий (например, epoll_wait). Это не учитывает использование ЦП. Если цикл событий ещё не запущен (например, в первом цикле основного скрипта), свойство имеет значение 0.
performanceNodeTiming.loopExit
Отметка времени в миллисекундах с высоким разрешением, в которой Node.js цикл событий завершился. Если цикл событий ещё не завершен, свойство имеет значение -1. Оно может иметь значение, отличное от -1, только в обработчике события 'exit'.
performanceNodeTiming.loopStart
Отметка времени в миллисекундах с высоким разрешением, в которой начался цикл событий Node.js. Если цикл событий ещё не начался (например, в первом цикле основного скрипта), свойство имеет значение -1.
performanceNodeTiming.nodeStart
Отметка времени в миллисекундах с высоким разрешением, в которой был инициализирован процесс Node.js.
performanceNodeTiming.v8Start
Отметка времени в миллисекундах с высоким разрешением, в которой была инициализирована платформа V8.
Класс: perf_hooks.PerformanceObserver
new PerformanceObserver(callback)
-
callback<Функция>-
list<PerformanceObserverEntryList> -
observer<PerformanceObserver>
-
Объекты PerformanceObserver предоставляют уведомления, когда новые экземпляры PerformanceEntry добавлены в временную шкалу производительности.
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const obs = new PerformanceObserver((list, observer) => {
console.log(list.getEntries());
observer.disconnect();
});
obs.observe({ entryTypes: ['mark'], buffered: true });
performance.mark('test'); Поскольку экземпляры PerformanceObserver вводят свой дополнительный накладные расходы на производительность, экземпляры не должны подписываться на уведомления постоянно. Пользователи должны отключать наблюдатели, как только они больше не нужны.
Функция callback вызывается, когда PerformanceObserver получает уведомление о новых экземплярах PerformanceEntry. Обработчик получает экземпляр PerformanceObserverEntryList и ссылку на экземпляр PerformanceObserver.
performanceObserver.disconnect()
Отключает экземпляр PerformanceObserver от всех уведомлений.
performanceObserver.observe(options)
-
options<Объект>-
entryTypes<массив строк> Массив строк, определяющих типы экземпляровPerformanceEntry, которые интересуют наблюдателя. Если не указан, будет выброшено исключение. -
buffered<логическое значение> Если true, обратный вызов уведомления будет вызван асинхронно, и уведомления о нескольких экземплярахPerformanceEntryбудут буферизованы внутри. Если false, уведомления будут мгновенными и синхронными. По умолчанию:false.
-
Подписывает экземпляр PerformanceObserver на уведомления о новых экземплярах PerformanceEntry, идентифицированных по options.entryTypes.
Когда options.buffered является false, callback будет вызван один раз для каждого экземпляра PerformanceEntry.
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const obs = new PerformanceObserver((list, observer) => {
// Called three times synchronously. `list` contains one item.
});
obs.observe({ entryTypes: ['mark'] });
for (let n = 0; n < 3; n++)
performance.mark(`test${n}`); const {
performance,
PerformanceObserver
} = require('perf_hooks');
const obs = new PerformanceObserver((list, observer) => {
// Called once. `list` contains three items.
});
obs.observe({ entryTypes: ['mark'], buffered: true });
for (let n = 0; n < 3; n++)
performance.mark(`test${n}`); Класс: PerformanceObserverEntryList
Класс PerformanceObserverEntryList используется для доступа к экземплярам PerformanceEntry, переданным в PerformanceObserver. Конструктор этого класса не доступен пользователю.
performanceObserverEntryList.getEntries()
- Возвращает: <PerformanceEntry[]>
Возвращает список объектов PerformanceEntry в хронологическом порядке относительно performanceEntry.startTime.
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const obs = new PerformanceObserver((perfObserverList, observer) => {
console.log(perfObserverList.getEntries());
/**
* [
* PerformanceEntry {
* name: 'test',
* entryType: 'mark',
* startTime: 81.465639,
* duration: 0
* },
* PerformanceEntry {
* name: 'meow',
* entryType: 'mark',
* startTime: 81.860064,
* duration: 0
* }
* ]
*/
observer.disconnect();
});
obs.observe({ entryTypes: ['mark'], buffered: true });
performance.mark('test');
performance.mark('meow'); performanceObserverEntryList.getEntriesByName(name[, type])
-
name<строка> -
type<строка> - Возвращает: <PerformanceEntry[]>
Возвращает список объектов PerformanceEntry в хронологическом порядке относительно performanceEntry.startTime, чьё поле performanceEntry.name равно name, и необязательно, чьё поле performanceEntry.entryType равно type.
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const obs = new PerformanceObserver((perfObserverList, observer) => {
console.log(perfObserverList.getEntriesByName('meow'));
/**
* [
* PerformanceEntry {
* name: 'meow',
* entryType: 'mark',
* startTime: 98.545991,
* duration: 0
* }
* ]
*/
console.log(perfObserverList.getEntriesByName('nope')); // []
console.log(perfObserverList.getEntriesByName('test', 'mark'));
/**
* [
* PerformanceEntry {
* name: 'test',
* entryType: 'mark',
* startTime: 63.518931,
* duration: 0
* }
* ]
*/
console.log(perfObserverList.getEntriesByName('test', 'measure')); // []
observer.disconnect();
});
obs.observe({ entryTypes: ['mark', 'measure'], buffered: true });
performance.mark('test');
performance.mark('meow'); performanceObserverEntryList.getEntriesByType(type)
-
type<строка> - Возвращает: <PerformanceEntry[]>
Возвращает список объектов PerformanceEntry в хронологическом порядке относительно performanceEntry.startTime, чьё поле performanceEntry.entryType равно type.
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const obs = new PerformanceObserver((perfObserverList, observer) => {
console.log(perfObserverList.getEntriesByType('mark'));
/**
* [
* PerformanceEntry {
* name: 'test',
* entryType: 'mark',
* startTime: 55.897834,
* duration: 0
* },
* PerformanceEntry {
* name: 'meow',
* entryType: 'mark',
* startTime: 56.350146,
* duration: 0
* }
* ]
*/
observer.disconnect();
});
obs.observe({ entryTypes: ['mark'], buffered: true });
performance.mark('test');
performance.mark('meow'); perf_hooks.monitorEventLoopDelay([options])
-
options<Объект>-
resolution<число> Частота выборки в миллисекундах. Должно быть больше нуля. По умолчанию:10.
-
- Возвращает: <Гистограмма>
Это свойство расширение Node.js. Оно недоступно в браузерах.
Создаёт объект Histogram для отслеживания и отчёта о задержке цикла событий со временем. Задержки будут сообщаться в наносекундах.
Использование таймера для определения приблизительной задержки цикла событий работает, потому что выполнение таймеров привязано к жизненному циклу цикла событий libuv. То есть, задержка в цикле вызовет задержку в выполнении таймера, и именно эти задержки предназначены для обнаружения этим API.
const { monitorEventLoopDelay } = require('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)); Класс: Histogram
Отслеживает задержку цикла событий с заданной частотой выборки. Конструктор этого класса недоступен пользователю.
Это свойство расширение Node.js. Оно недоступно в браузерах.
histogram.disable()
- Возвращает: <логическое значение>
Отключает таймер отбора проб задержки цикла событий. Возвращает true, если таймер остановлен, false, если он уже остановлен.
histogram.enable()
- Возвращает: <логическое значение>
Включает таймер отбора проб задержки цикла событий. Возвращает true, если таймер запущен, false, если он уже запущен.
histogram.exceeds
Количество раз, когда задержка цикла событий превысила максимальный порог задержки цикла событий в 1 час.
histogram.max
Максимальная зарегистрированная задержка цикла событий.
histogram.mean
Среднее значение зарегистрированных задержек цикла событий.
histogram.min
Минимальная зарегистрированная задержка цикла событий.
histogram.percentile(percentile)
Возвращает значение в заданном процентиле.
histogram.percentiles
Возвращает объект Map, содержащий детали накопленного распределения процентилей.
histogram.reset()
Сбрасывает собранные данные гистограммы.
histogram.stddev
Среднеквадратическое отклонение зарегистрированных задержек цикла событий.
Примеры
Измерение продолжительности асинхронных операций
В следующем примере используются Async Hooks и Performance API для измерения фактической продолжительности операции Timeout (включая время выполнения обратного вызова).
'use strict';
const async_hooks = require('async_hooks');
const {
performance,
PerformanceObserver
} = require('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();
observer.disconnect();
});
obs.observe({ entryTypes: ['measure'], buffered: true });
setTimeout(() => {}, 1000); Измерение времени загрузки зависимостей
В следующем примере измеряется продолжительность операций require() для загрузки зависимостей:
'use strict';
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const mod = require('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);
});
obs.disconnect();
});
obs.observe({ entryTypes: ['function'], buffered: true });
require('some-module');
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v14.x/docs/api/perf_hooks.html