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