Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 155 additions & 110 deletions doc/api/perf_hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ the Resource Timeline. If `name` is provided, removes only the named resource.
added:
- v14.10.0
- v12.19.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/60370
description: Added `perf_hooks.eventLoopUtilization` alias.
-->

* `utilization1` {Object} The result of a previous call to
Expand All @@ -131,62 +135,9 @@ added:
* `active` {number}
* `utilization` {number}

The `eventLoopUtilization()` method returns an object that contains the
cumulative duration of time the event loop has been both idle and active as a
high resolution milliseconds timer. The `utilization` value is the calculated
Event Loop Utilization (ELU).

If bootstrapping has not yet finished on the main thread the properties have
the value of `0`. The ELU is immediately available on [Worker threads][] since
bootstrap happens within the event loop.

Both `utilization1` and `utilization2` are optional parameters.

If `utilization1` is passed, then the delta between the current call's `active`
and `idle` times, as well as the corresponding `utilization` value are
calculated and returned (similar to [`process.hrtime()`][]).
This is an alias of [`perf_hooks.eventLoopUtilization()`][].

If `utilization1` and `utilization2` are both passed, then the delta is
calculated between the two arguments. This is a convenience option because,
unlike [`process.hrtime()`][], calculating the ELU is more complex than a
single subtraction.

ELU is similar to CPU utilization, except that it only measures event loop
statistics and not CPU usage. It represents the percentage of time the event
loop has spent outside the event loop's event provider (e.g. `epoll_wait`).
No other CPU idle time is taken into consideration. The following is an example
of how a mostly idle process will have a high ELU.

```mjs
import { eventLoopUtilization } from 'node:perf_hooks';
import { spawnSync } from 'node:child_process';

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

```cjs
'use strict';
const { eventLoopUtilization } = require('node:perf_hooks').performance;
const { spawnSync } = require('node:child_process');

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

Although the CPU is mostly idle while running this script, the value of
`utilization` is `1`. This is because the call to
[`child_process.spawnSync()`][] blocks the event loop from proceeding.

Passing in a user-defined object instead of the result of a previous call to
`eventLoopUtilization()` will lead to undefined behavior. The return values
are not guaranteed to reflect any correct state of the event loop.
_This property is an extension by Node.js. It is not available in Web browsers._

### `performance.getEntries()`

Expand Down Expand Up @@ -424,6 +375,9 @@ which the current `node` process began, measured in Unix time.
<!-- YAML
added: v8.5.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/60370
description: Added `perf_hooks.timerify` alias.
- version: v16.0.0
pr-url: https://github.com/nodejs/node/pull/37475
description: Added the histogram option.
Expand All @@ -439,62 +393,9 @@ changes:
`perf_hooks.createHistogram()` that will record runtime durations in
nanoseconds.

_This property is an extension by Node.js. It is not available in Web browsers._
This is an alias of [`perf_hooks.timerify()`][].

Wraps a function within a new function that measures the running time of the
wrapped function. A `PerformanceObserver` must be subscribed to the `'function'`
event type in order for the timing details to be accessed.

```mjs
import { performance, PerformanceObserver } from 'node:perf_hooks';

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

const wrapped = performance.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();
```

```cjs
const {
performance,
PerformanceObserver,
} = require('node:perf_hooks');

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

const wrapped = performance.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();
```

If the wrapped function returns a promise, a finally handler will be attached
to the promise and the duration will be reported once the finally handler is
invoked.
_This property is an extension by Node.js. It is not available in Web browsers._

### `performance.toJSON()`

Expand Down Expand Up @@ -1722,6 +1623,78 @@ added:

Returns a {RecordableHistogram}.

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

<!-- YAML
added: REPLACEME
-->

* `utilization1` {Object} The result of a previous call to
`eventLoopUtilization()`.
* `utilization2` {Object} The result of a previous call to
`eventLoopUtilization()` prior to `utilization1`.
* Returns: {Object}
* `idle` {number}
* `active` {number}
* `utilization` {number}

The `eventLoopUtilization()` function returns an object that contains the
cumulative duration of time the event loop has been both idle and active as a
high resolution milliseconds timer. The `utilization` value is the calculated
Event Loop Utilization (ELU).

If bootstrapping has not yet finished on the main thread the properties have
the value of `0`. The ELU is immediately available on [Worker threads][] since
bootstrap happens within the event loop.

Both `utilization1` and `utilization2` are optional parameters.

If `utilization1` is passed, then the delta between the current call's `active`
and `idle` times, as well as the corresponding `utilization` value are
calculated and returned (similar to [`process.hrtime()`][]).

If `utilization1` and `utilization2` are both passed, then the delta is
calculated between the two arguments. This is a convenience option because,
unlike [`process.hrtime()`][], calculating the ELU is more complex than a
single subtraction.

ELU is similar to CPU utilization, except that it only measures event loop
statistics and not CPU usage. It represents the percentage of time the event
loop has spent outside the event loop's event provider (e.g. `epoll_wait`).
No other CPU idle time is taken into consideration. The following is an example
of how a mostly idle process will have a high ELU.

```mjs
import { eventLoopUtilization } from 'node:perf_hooks';
import { spawnSync } from 'node:child_process';

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

```cjs
'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);
});
```

Although the CPU is mostly idle while running this script, the value of
`utilization` is `1`. This is because the call to
[`child_process.spawnSync()`][] blocks the event loop from proceeding.

Passing in a user-defined object instead of the result of a previous call to
`eventLoopUtilization()` will lead to undefined behavior. The return values
are not guaranteed to reflect any correct state of the event loop.

## `perf_hooks.monitorEventLoopDelay([options])`

<!-- YAML
Expand Down Expand Up @@ -1775,6 +1748,76 @@ console.log(h.percentile(50));
console.log(h.percentile(99));
```

## `perf_hooks.timerify(fn[, options])`

<!-- YAML
added: REPLACEME
-->

* `fn` {Function}
* `options` {Object}
* `histogram` {RecordableHistogram} A histogram object created using
`perf_hooks.createHistogram()` that will record runtime durations in
nanoseconds.

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

Wraps a function within a new function that measures the running time of the
wrapped function. A `PerformanceObserver` must be subscribed to the `'function'`
event type in order for the timing details to be accessed.

```mjs
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();
```

```cjs
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();
```

If the wrapped function returns a promise, a finally handler will be attached
to the promise and the duration will be reported once the finally handler is
invoked.

## Class: `Histogram`

<!-- YAML
Expand Down Expand Up @@ -2306,6 +2349,8 @@ dns.promises.resolve('localhost');
[Worker threads]: worker_threads.md#worker-threads
[`'exit'`]: process.md#event-exit
[`child_process.spawnSync()`]: child_process.md#child_processspawnsynccommand-args-options
[`perf_hooks.eventLoopUtilization()`]: #perf_hookseventlooputilizationutilization1-utilization2
[`perf_hooks.timerify()`]: #perf_hookstimerifyfn-options
[`process.hrtime()`]: process.md#processhrtimetime
[`timeOrigin`]: https://w3c.github.io/hr-time/#dom-performance-timeorigin
[`window.performance.toJSON`]: https://developer.mozilla.org/en-US/docs/Web/API/Performance/toJSON
Expand Down
5 changes: 2 additions & 3 deletions doc/api/worker_threads.md
Original file line number Diff line number Diff line change
Expand Up @@ -1838,7 +1838,7 @@ added:
-->

An object that can be used to query performance information from a worker
instance. Similar to [`perf_hooks.performance`][].
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worker.performance is not similar to perf_hooks.performance at all. It has only one method eventLoopUtilization.

instance.

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

Expand Down Expand Up @@ -2240,8 +2240,7 @@ thread spawned will spawn another until the application crashes.
[`fs.open()`]: fs.md#fsopenpath-flags-mode-callback
[`markAsUntransferable()`]: #worker_threadsmarkasuntransferableobject
[`node:cluster` module]: cluster.md
[`perf_hooks.performance`]: perf_hooks.md#perf_hooksperformance
[`perf_hooks` `eventLoopUtilization()`]: perf_hooks.md#performanceeventlooputilizationutilization1-utilization2
[`perf_hooks` `eventLoopUtilization()`]: perf_hooks.md#perf_hookseventlooputilizationutilization1-utilization2
[`port.on('message')`]: #event-message
[`port.onmessage()`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/onmessage
[`port.postMessage()`]: #portpostmessagevalue-transferlist
Expand Down
4 changes: 4 additions & 0 deletions lib/perf_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const {
} = require('internal/histogram');

const monitorEventLoopDelay = require('internal/perf/event_loop_delay');
const { eventLoopUtilization } = require('internal/perf/event_loop_utilization');
const timerify = require('internal/perf/timerify');

module.exports = {
Performance,
Expand All @@ -38,6 +40,8 @@ module.exports = {
PerformanceObserverEntryList,
PerformanceResourceTiming,
monitorEventLoopDelay,
eventLoopUtilization,
timerify,
createHistogram,
performance,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ const TIMEOUT = 10;
const SPIN_DUR = 50;

const assert = require('assert');
const { performance } = require('perf_hooks');
const { performance, eventLoopUtilization } = require('perf_hooks');
const { Worker, parentPort } = require('worker_threads');

const { nodeTiming, eventLoopUtilization } = performance;
// Verifies that `performance.eventLoopUtilization` is an alias of
// `perf_hooks.eventLoopUtilization`.
assert.strictEqual(performance.eventLoopUtilization, eventLoopUtilization);
const { nodeTiming } = performance;
const elu = eventLoopUtilization();

// Take into account whether this test was started as a Worker.
Expand Down
Loading
Loading