• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Performance measurement APIs
2
3<!--introduced_in=v8.5.0-->
4
5> Stability: 2 - Stable
6
7<!-- source_link=lib/perf_hooks.js -->
8
9This module provides an implementation of a subset of the W3C
10[Web Performance APIs][] as well as additional APIs for
11Node.js-specific performance measurements.
12
13Node.js supports the following [Web Performance APIs][]:
14
15* [High Resolution Time][]
16* [Performance Timeline][]
17* [User Timing][]
18
19```js
20const { PerformanceObserver, performance } = require('perf_hooks');
21
22const obs = new PerformanceObserver((items) => {
23  console.log(items.getEntries()[0].duration);
24  performance.clearMarks();
25});
26obs.observe({ entryTypes: ['measure'] });
27performance.measure('Start to Now');
28
29performance.mark('A');
30doSomeLongRunningProcess(() => {
31  performance.measure('A to Now', 'A');
32
33  performance.mark('B');
34  performance.measure('A to B', 'A', 'B');
35});
36```
37
38## `perf_hooks.performance`
39<!-- YAML
40added: v8.5.0
41-->
42
43An object that can be used to collect performance metrics from the current
44Node.js instance. It is similar to [`window.performance`][] in browsers.
45
46### `performance.clearMarks([name])`
47<!-- YAML
48added: v8.5.0
49-->
50
51* `name` {string}
52
53If `name` is not provided, removes all `PerformanceMark` objects from the
54Performance Timeline. If `name` is provided, removes only the named mark.
55
56### `performance.mark([name])`
57<!-- YAML
58added: v8.5.0
59-->
60
61* `name` {string}
62
63Creates a new `PerformanceMark` entry in the Performance Timeline. A
64`PerformanceMark` is a subclass of `PerformanceEntry` whose
65`performanceEntry.entryType` is always `'mark'`, and whose
66`performanceEntry.duration` is always `0`. Performance marks are used
67to mark specific significant moments in the Performance Timeline.
68
69### `performance.measure(name[, startMark[, endMark]])`
70<!-- YAML
71added: v8.5.0
72changes:
73  - version: v12.16.3
74    pr-url: https://github.com/nodejs/node/pull/32651
75    description: Make `startMark` and `endMark` parameters optional.
76-->
77
78* `name` {string}
79* `startMark` {string} Optional.
80* `endMark` {string} Optional.
81
82Creates a new `PerformanceMeasure` entry in the Performance Timeline. A
83`PerformanceMeasure` is a subclass of `PerformanceEntry` whose
84`performanceEntry.entryType` is always `'measure'`, and whose
85`performanceEntry.duration` measures the number of milliseconds elapsed since
86`startMark` and `endMark`.
87
88The `startMark` argument may identify any *existing* `PerformanceMark` in the
89Performance Timeline, or *may* identify any of the timestamp properties
90provided by the `PerformanceNodeTiming` class. If the named `startMark` does
91not exist, then `startMark` is set to [`timeOrigin`][] by default.
92
93The optional `endMark` argument must identify any *existing* `PerformanceMark`
94in the Performance Timeline or any of the timestamp properties provided by the
95`PerformanceNodeTiming` class. `endMark` will be `performance.now()`
96if no parameter is passed, otherwise if the named `endMark` does not exist, an
97error will be thrown.
98
99### `performance.nodeTiming`
100<!-- YAML
101added: v8.5.0
102-->
103
104* {PerformanceNodeTiming}
105
106_This property is an extension by Node.js. It is not available in Web browsers._
107
108An instance of the `PerformanceNodeTiming` class that provides performance
109metrics for specific Node.js operational milestones.
110
111### `performance.now()`
112<!-- YAML
113added: v8.5.0
114-->
115
116* Returns: {number}
117
118Returns the current high resolution millisecond timestamp, where 0 represents
119the start of the current `node` process.
120
121### `performance.timeOrigin`
122<!-- YAML
123added: v8.5.0
124-->
125
126* {number}
127
128The [`timeOrigin`][] specifies the high resolution millisecond timestamp at
129which the current `node` process began, measured in Unix time.
130
131### `performance.timerify(fn)`
132<!-- YAML
133added: v8.5.0
134-->
135
136* `fn` {Function}
137
138_This property is an extension by Node.js. It is not available in Web browsers._
139
140Wraps a function within a new function that measures the running time of the
141wrapped function. A `PerformanceObserver` must be subscribed to the `'function'`
142event type in order for the timing details to be accessed.
143
144```js
145const {
146  performance,
147  PerformanceObserver
148} = require('perf_hooks');
149
150function someFunction() {
151  console.log('hello world');
152}
153
154const wrapped = performance.timerify(someFunction);
155
156const obs = new PerformanceObserver((list) => {
157  console.log(list.getEntries()[0].duration);
158  obs.disconnect();
159});
160obs.observe({ entryTypes: ['function'] });
161
162// A performance timeline entry will be created
163wrapped();
164```
165
166### `performance.eventLoopUtilization([util1][,util2])`
167<!-- YAML
168added: v12.19.0
169-->
170
171* `util1` {Object} The result of a previous call to `eventLoopUtilization()`
172* `util2` {Object} The result of a previous call to `eventLoopUtilization()`
173    prior to `util1`
174* Returns {Object}
175  * `idle` {number}
176  * `active` {number}
177  * `utilization` {number}
178
179The `eventLoopUtilization()` method returns an object that contains the
180cumulative duration of time the event loop has been both idle and active as a
181high resolution milliseconds timer. The `utilization` value is the calculated
182Event Loop Utilization (ELU). If bootstrapping has not yet finished, the
183properties have the value of 0.
184
185`util1` and `util2` are optional parameters.
186
187If `util1` is passed then the delta between the current call's `active` and
188`idle` times are calculated and returned (similar to [`process.hrtime()`][]).
189Likewise the adjusted `utilization` value is calculated.
190
191If `util1` and `util2` are both passed then the calculation adjustments are
192done between the two arguments. This is a convenience option because unlike
193[`process.hrtime()`][] additional work is done to calculate the ELU.
194
195ELU is similar to CPU utilization except that it is calculated using high
196precision wall-clock time. It represents the percentage of time the event loop
197has spent outside the event loop's event provider (e.g. `epoll_wait`). No other
198CPU idle time is taken into consideration. The following is an example of how
199a mostly idle process will have a high ELU.
200
201<!-- eslint-skip -->
202```js
203'use strict';
204const { eventLoopUtilization } = require('perf_hooks').performance;
205const { spawnSync } = require('child_process');
206
207setImmediate(() => {
208  const elu = eventLoopUtilization();
209  spawnSync('sleep', ['5']);
210  console.log(eventLoopUtilization(elu).utilization);
211});
212```
213
214Although the CPU is mostly idle while running this script, the value of
215`utilization` is 1. This is because the call to [`child_process.spawnSync()`][]
216blocks the event loop from proceeding.
217
218Passing in a user-defined object instead of the result of a previous call to
219`eventLoopUtilization()` will lead to undefined behavior. The return values
220are not guaranteed to reflect any correct state of the event loop.
221
222## Class: `PerformanceEntry`
223<!-- YAML
224added: v8.5.0
225-->
226
227### `performanceEntry.duration`
228<!-- YAML
229added: v8.5.0
230-->
231
232* {number}
233
234The total number of milliseconds elapsed for this entry. This value will not
235be meaningful for all Performance Entry types.
236
237### `performanceEntry.name`
238<!-- YAML
239added: v8.5.0
240-->
241
242* {string}
243
244The name of the performance entry.
245
246### `performanceEntry.startTime`
247<!-- YAML
248added: v8.5.0
249-->
250
251* {number}
252
253The high resolution millisecond timestamp marking the starting time of the
254Performance Entry.
255
256### `performanceEntry.entryType`
257<!-- YAML
258added: v8.5.0
259-->
260
261* {string}
262
263The type of the performance entry. It may be one of:
264
265* `'node'` (Node.js only)
266* `'mark'` (available on the Web)
267* `'measure'` (available on the Web)
268* `'gc'` (Node.js only)
269* `'function'` (Node.js only)
270* `'http2'` (Node.js only)
271* `'http'` (Node.js only)
272
273### `performanceEntry.kind`
274<!-- YAML
275added: v8.5.0
276-->
277
278* {number}
279
280_This property is an extension by Node.js. It is not available in Web browsers._
281
282When `performanceEntry.entryType` is equal to `'gc'`, the `performance.kind`
283property identifies the type of garbage collection operation that occurred.
284The value may be one of:
285
286* `perf_hooks.constants.NODE_PERFORMANCE_GC_MAJOR`
287* `perf_hooks.constants.NODE_PERFORMANCE_GC_MINOR`
288* `perf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTAL`
289* `perf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB`
290
291### performanceEntry.flags
292<!-- YAML
293added: v12.17.0
294-->
295
296* {number}
297
298_This property is an extension by Node.js. It is not available in Web browsers._
299
300When `performanceEntry.entryType` is equal to `'gc'`, the `performance.flags`
301property contains additional information about garbage collection operation.
302The value may be one of:
303
304* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_NO`
305* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED`
306* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_FORCED`
307* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING`
308* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE`
309* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY`
310* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE`
311
312## Class: `PerformanceNodeTiming extends PerformanceEntry`
313<!-- YAML
314added: v8.5.0
315-->
316
317_This property is an extension by Node.js. It is not available in Web browsers._
318
319Provides timing details for Node.js itself. The constructor of this class
320is not exposed to users.
321
322### `performanceNodeTiming.bootstrapComplete`
323<!-- YAML
324added: v8.5.0
325-->
326
327* {number}
328
329The high resolution millisecond timestamp at which the Node.js process
330completed bootstrapping. If bootstrapping has not yet finished, the property
331has the value of -1.
332
333### `performanceNodeTiming.environment`
334<!-- YAML
335added: v8.5.0
336-->
337
338* {number}
339
340The high resolution millisecond timestamp at which the Node.js environment was
341initialized.
342
343### `performanceNodeTiming.loopExit`
344<!-- YAML
345added: v8.5.0
346-->
347
348* {number}
349
350The high resolution millisecond timestamp at which the Node.js event loop
351exited. If the event loop has not yet exited, the property has the value of -1.
352It can only have a value of not -1 in a handler of the [`'exit'`][] event.
353
354### `performanceNodeTiming.loopStart`
355<!-- YAML
356added: v8.5.0
357-->
358
359* {number}
360
361The high resolution millisecond timestamp at which the Node.js event loop
362started. If the event loop has not yet started (e.g., in the first tick of the
363main script), the property has the value of -1.
364
365### `performanceNodeTiming.nodeStart`
366<!-- YAML
367added: v8.5.0
368-->
369
370* {number}
371
372The high resolution millisecond timestamp at which the Node.js process was
373initialized.
374
375### `performanceNodeTiming.v8Start`
376<!-- YAML
377added: v8.5.0
378-->
379
380* {number}
381
382The high resolution millisecond timestamp at which the V8 platform was
383initialized.
384
385### `performanceNodeTiming.idleTime`
386<!-- YAML
387added: v12.19.0
388-->
389
390* {number}
391
392The high resolution millisecond timestamp of the amount of time the event loop
393has been idle within the event loop's event provider (e.g. `epoll_wait`). This
394does not take CPU usage into consideration. If the event loop has not yet
395started (e.g., in the first tick of the main script), the property has the
396value of 0.
397
398## Class: `perf_hooks.PerformanceObserver`
399
400### `new PerformanceObserver(callback)`
401<!-- YAML
402added: v8.5.0
403-->
404
405* `callback` {Function}
406  * `list` {PerformanceObserverEntryList}
407  * `observer` {PerformanceObserver}
408
409`PerformanceObserver` objects provide notifications when new
410`PerformanceEntry` instances have been added to the Performance Timeline.
411
412```js
413const {
414  performance,
415  PerformanceObserver
416} = require('perf_hooks');
417
418const obs = new PerformanceObserver((list, observer) => {
419  console.log(list.getEntries());
420  observer.disconnect();
421});
422obs.observe({ entryTypes: ['mark'], buffered: true });
423
424performance.mark('test');
425```
426
427Because `PerformanceObserver` instances introduce their own additional
428performance overhead, instances should not be left subscribed to notifications
429indefinitely. Users should disconnect observers as soon as they are no
430longer needed.
431
432The `callback` is invoked when a `PerformanceObserver` is
433notified about new `PerformanceEntry` instances. The callback receives a
434`PerformanceObserverEntryList` instance and a reference to the
435`PerformanceObserver`.
436
437### `performanceObserver.disconnect()`
438<!-- YAML
439added: v8.5.0
440-->
441Disconnects the `PerformanceObserver` instance from all notifications.
442
443### `performanceObserver.observe(options)`
444<!-- YAML
445added: v8.5.0
446-->
447
448* `options` {Object}
449  * `entryTypes` {string[]} An array of strings identifying the types of
450    `PerformanceEntry` instances the observer is interested in. If not
451    provided an error will be thrown.
452  * `buffered` {boolean} If true, the notification callback will be
453    called using `setImmediate()` and multiple `PerformanceEntry` instance
454    notifications will be buffered internally. If `false`, notifications will
455    be immediate and synchronous. **Default:** `false`.
456
457Subscribes the `PerformanceObserver` instance to notifications of new
458`PerformanceEntry` instances identified by `options.entryTypes`.
459
460When `options.buffered` is `false`, the `callback` will be invoked once for
461every `PerformanceEntry` instance:
462
463```js
464const {
465  performance,
466  PerformanceObserver
467} = require('perf_hooks');
468
469const obs = new PerformanceObserver((list, observer) => {
470  // Called three times synchronously. `list` contains one item.
471});
472obs.observe({ entryTypes: ['mark'] });
473
474for (let n = 0; n < 3; n++)
475  performance.mark(`test${n}`);
476```
477
478```js
479const {
480  performance,
481  PerformanceObserver
482} = require('perf_hooks');
483
484const obs = new PerformanceObserver((list, observer) => {
485  // Called once. `list` contains three items.
486});
487obs.observe({ entryTypes: ['mark'], buffered: true });
488
489for (let n = 0; n < 3; n++)
490  performance.mark(`test${n}`);
491```
492
493## Class: `PerformanceObserverEntryList`
494<!-- YAML
495added: v8.5.0
496-->
497
498The `PerformanceObserverEntryList` class is used to provide access to the
499`PerformanceEntry` instances passed to a `PerformanceObserver`.
500The constructor of this class is not exposed to users.
501
502### `performanceObserverEntryList.getEntries()`
503<!-- YAML
504added: v8.5.0
505-->
506
507* Returns: {PerformanceEntry[]}
508
509Returns a list of `PerformanceEntry` objects in chronological order
510with respect to `performanceEntry.startTime`.
511
512### `performanceObserverEntryList.getEntriesByName(name[, type])`
513<!-- YAML
514added: v8.5.0
515-->
516
517* `name` {string}
518* `type` {string}
519* Returns: {PerformanceEntry[]}
520
521Returns a list of `PerformanceEntry` objects in chronological order
522with respect to `performanceEntry.startTime` whose `performanceEntry.name` is
523equal to `name`, and optionally, whose `performanceEntry.entryType` is equal to
524`type`.
525
526### `performanceObserverEntryList.getEntriesByType(type)`
527<!-- YAML
528added: v8.5.0
529-->
530
531* `type` {string}
532* Returns: {PerformanceEntry[]}
533
534Returns a list of `PerformanceEntry` objects in chronological order
535with respect to `performanceEntry.startTime` whose `performanceEntry.entryType`
536is equal to `type`.
537
538## `perf_hooks.monitorEventLoopDelay([options])`
539<!-- YAML
540added: v11.10.0
541-->
542
543* `options` {Object}
544  * `resolution` {number} The sampling rate in milliseconds. Must be greater
545    than zero. **Default:** `10`.
546* Returns: {Histogram}
547
548_This property is an extension by Node.js. It is not available in Web browsers._
549
550Creates a `Histogram` object that samples and reports the event loop delay
551over time. The delays will be reported in nanoseconds.
552
553Using a timer to detect approximate event loop delay works because the
554execution of timers is tied specifically to the lifecycle of the libuv
555event loop. That is, a delay in the loop will cause a delay in the execution
556of the timer, and those delays are specifically what this API is intended to
557detect.
558
559```js
560const { monitorEventLoopDelay } = require('perf_hooks');
561const h = monitorEventLoopDelay({ resolution: 20 });
562h.enable();
563// Do something.
564h.disable();
565console.log(h.min);
566console.log(h.max);
567console.log(h.mean);
568console.log(h.stddev);
569console.log(h.percentiles);
570console.log(h.percentile(50));
571console.log(h.percentile(99));
572```
573
574### Class: `Histogram`
575<!-- YAML
576added: v11.10.0
577-->
578Tracks the event loop delay at a given sampling rate. The constructor of
579this class not exposed to users.
580
581_This property is an extension by Node.js. It is not available in Web browsers._
582
583#### `histogram.disable()`
584<!-- YAML
585added: v11.10.0
586-->
587
588* Returns: {boolean}
589
590Disables the event loop delay sample timer. Returns `true` if the timer was
591stopped, `false` if it was already stopped.
592
593#### `histogram.enable()`
594<!-- YAML
595added: v11.10.0
596-->
597
598* Returns: {boolean}
599
600Enables the event loop delay sample timer. Returns `true` if the timer was
601started, `false` if it was already started.
602
603#### `histogram.exceeds`
604<!-- YAML
605added: v11.10.0
606-->
607
608* {number}
609
610The number of times the event loop delay exceeded the maximum 1 hour event
611loop delay threshold.
612
613#### `histogram.max`
614<!-- YAML
615added: v11.10.0
616-->
617
618* {number}
619
620The maximum recorded event loop delay.
621
622#### `histogram.mean`
623<!-- YAML
624added: v11.10.0
625-->
626
627* {number}
628
629The mean of the recorded event loop delays.
630
631#### `histogram.min`
632<!-- YAML
633added: v11.10.0
634-->
635
636* {number}
637
638The minimum recorded event loop delay.
639
640#### `histogram.percentile(percentile)`
641<!-- YAML
642added: v11.10.0
643-->
644
645* `percentile` {number} A percentile value between 1 and 100.
646* Returns: {number}
647
648Returns the value at the given percentile.
649
650#### `histogram.percentiles`
651<!-- YAML
652added: v11.10.0
653-->
654
655* {Map}
656
657Returns a `Map` object detailing the accumulated percentile distribution.
658
659#### `histogram.reset()`
660<!-- YAML
661added: v11.10.0
662-->
663
664Resets the collected histogram data.
665
666#### `histogram.stddev`
667<!-- YAML
668added: v11.10.0
669-->
670
671* {number}
672
673The standard deviation of the recorded event loop delays.
674
675## Examples
676
677### Measuring the duration of async operations
678
679The following example uses the [Async Hooks][] and Performance APIs to measure
680the actual duration of a Timeout operation (including the amount of time it took
681to execute the callback).
682
683```js
684'use strict';
685const async_hooks = require('async_hooks');
686const {
687  performance,
688  PerformanceObserver
689} = require('perf_hooks');
690
691const set = new Set();
692const hook = async_hooks.createHook({
693  init(id, type) {
694    if (type === 'Timeout') {
695      performance.mark(`Timeout-${id}-Init`);
696      set.add(id);
697    }
698  },
699  destroy(id) {
700    if (set.has(id)) {
701      set.delete(id);
702      performance.mark(`Timeout-${id}-Destroy`);
703      performance.measure(`Timeout-${id}`,
704                          `Timeout-${id}-Init`,
705                          `Timeout-${id}-Destroy`);
706    }
707  }
708});
709hook.enable();
710
711const obs = new PerformanceObserver((list, observer) => {
712  console.log(list.getEntries()[0]);
713  performance.clearMarks();
714  observer.disconnect();
715});
716obs.observe({ entryTypes: ['measure'], buffered: true });
717
718setTimeout(() => {}, 1000);
719```
720
721### Measuring how long it takes to load dependencies
722
723The following example measures the duration of `require()` operations to load
724dependencies:
725
726<!-- eslint-disable no-global-assign -->
727```js
728'use strict';
729const {
730  performance,
731  PerformanceObserver
732} = require('perf_hooks');
733const mod = require('module');
734
735// Monkey patch the require function
736mod.Module.prototype.require =
737  performance.timerify(mod.Module.prototype.require);
738require = performance.timerify(require);
739
740// Activate the observer
741const obs = new PerformanceObserver((list) => {
742  const entries = list.getEntries();
743  entries.forEach((entry) => {
744    console.log(`require('${entry[0]}')`, entry.duration);
745  });
746  obs.disconnect();
747});
748obs.observe({ entryTypes: ['function'], buffered: true });
749
750require('some-module');
751```
752
753[`'exit'`]: process.html#process_event_exit
754[`process.hrtime()`]: process.html#process_process_hrtime_time
755[`child_process.spawnSync()`]: child_process.html#child_process_child_process_spawnsync_command_args_options
756[`timeOrigin`]: https://w3c.github.io/hr-time/#dom-performance-timeorigin
757[`window.performance`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/performance
758[Async Hooks]: async_hooks.html
759[High Resolution Time]: https://www.w3.org/TR/hr-time-2
760[Performance Timeline]: https://w3c.github.io/performance-timeline/
761[Web Performance APIs]: https://w3c.github.io/perf-timing-primer/
762[User Timing]: https://www.w3.org/TR/user-timing/
763