mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
perf_hooks: move non-standard performance properties to perf_hooks
`performance.eventLoopUtilization` and `performance.timerify` are not part of the Web API. Expose these two functions directly on the `perf_hooks` module. PR-URL: https://github.com/nodejs/node/pull/60370 Fixes: https://github.com/nodejs/node/issues/60368 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
1 parent
4fe325d93d
commit
51a57f2b36
6 files changed
+186
-131
No files matched your search
+155
-110
@@ -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
|
||||
@@ -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).
|
||||
This is an alias of [`perf_hooks.eventLoopUtilization()`][].
|
||||
|
||||
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').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()`
|
||||
|
||||
@@ -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.
|
||||
@@ -439,63 +393,10 @@ changes:
|
||||
`perf_hooks.createHistogram()` that will record runtime durations in
|
||||
nanoseconds.
|
||||
|
||||
This is an alias of [`perf_hooks.timerify()`][].
|
||||
|
||||
_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 { 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.
|
||||
|
||||
### `performance.toJSON()`
|
||||
|
||||
<!-- YAML
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -1838,7 +1838,7 @@ added:
|
||||
-->
|
||||
|
||||
An object that can be used to query performance information from a worker
|
||||
instance. Similar to [`perf_hooks.performance`][].
|
||||
instance.
|
||||
|
||||
#### `performance.eventLoopUtilization([utilization1[, utilization2]])`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
@@ -38,6 +40,8 @@ module.exports = {
|
||||
PerformanceObserverEntryList,
|
||||
PerformanceResourceTiming,
|
||||
monitorEventLoopDelay,
|
||||
eventLoopUtilization,
|
||||
timerify,
|
||||
createHistogram,
|
||||
performance,
|
||||
};
|
||||
|
||||
+5
-2
@@ -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.
|
||||
+19
-15
@@ -5,6 +5,7 @@ const assert = require('assert');
|
||||
|
||||
const {
|
||||
createHistogram,
|
||||
timerify,
|
||||
performance,
|
||||
PerformanceObserver
|
||||
} = require('perf_hooks');
|
||||
@@ -13,9 +14,12 @@ const {
|
||||
setTimeout: sleep
|
||||
} = require('timers/promises');
|
||||
|
||||
// Verifies that `performance.timerify` is an alias of `perf_hooks.timerify`.
|
||||
assert.strictEqual(performance.timerify, timerify);
|
||||
|
||||
{
|
||||
// Intentional non-op. Do not wrap in common.mustCall();
|
||||
const n = performance.timerify(function noop() {});
|
||||
const n = timerify(function noop() {});
|
||||
|
||||
const obs = new PerformanceObserver(common.mustCall((list) => {
|
||||
const entries = list.getEntries();
|
||||
@@ -36,7 +40,7 @@ const {
|
||||
// performance timeline entry will not be reported.
|
||||
const obs = new PerformanceObserver(common.mustNotCall());
|
||||
obs.observe({ entryTypes: ['function'] });
|
||||
const n = performance.timerify(() => {
|
||||
const n = timerify(() => {
|
||||
throw new Error('test');
|
||||
});
|
||||
assert.throws(() => n(), /^Error: test$/);
|
||||
@@ -45,7 +49,7 @@ const {
|
||||
|
||||
{
|
||||
class N {}
|
||||
const n = performance.timerify(N);
|
||||
const n = timerify(N);
|
||||
|
||||
const obs = new PerformanceObserver(common.mustCall((list) => {
|
||||
const entries = list.getEntries();
|
||||
@@ -66,7 +70,7 @@ const {
|
||||
|
||||
{
|
||||
[1, {}, [], null, undefined, Infinity].forEach((input) => {
|
||||
assert.throws(() => performance.timerify(input),
|
||||
assert.throws(() => timerify(input),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
@@ -78,9 +82,9 @@ const {
|
||||
// Function can be wrapped many times, also check length and name
|
||||
{
|
||||
const m = (a, b = 1) => {};
|
||||
const n = performance.timerify(m);
|
||||
const o = performance.timerify(m);
|
||||
const p = performance.timerify(n);
|
||||
const n = timerify(m);
|
||||
const o = timerify(m);
|
||||
const p = timerify(n);
|
||||
assert.notStrictEqual(n, o);
|
||||
assert.notStrictEqual(n, p);
|
||||
assert.notStrictEqual(o, p);
|
||||
@@ -97,7 +101,7 @@ const {
|
||||
for (let i = 0; i < 1e3; i++)
|
||||
_deadCode = i;
|
||||
};
|
||||
const n = performance.timerify(m, { histogram });
|
||||
const n = timerify(m, { histogram });
|
||||
assert.strictEqual(histogram.max, 0);
|
||||
for (let i = 0; i < 10; i++) {
|
||||
n();
|
||||
@@ -106,7 +110,7 @@ const {
|
||||
assert.ok(_deadCode >= 0);
|
||||
assert.notStrictEqual(histogram.max, 0);
|
||||
[1, '', {}, [], false].forEach((histogram) => {
|
||||
assert.throws(() => performance.timerify(m, { histogram }), {
|
||||
assert.throws(() => timerify(m, { histogram }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
@@ -117,14 +121,14 @@ const {
|
||||
const m = async (a, b = 1) => {
|
||||
await sleep(10);
|
||||
};
|
||||
const n = performance.timerify(m, { histogram });
|
||||
const n = timerify(m, { histogram });
|
||||
assert.strictEqual(histogram.max, 0);
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await n();
|
||||
}
|
||||
assert.notStrictEqual(histogram.max, 0);
|
||||
[1, '', {}, [], false].forEach((histogram) => {
|
||||
assert.throws(() => performance.timerify(m, { histogram }), {
|
||||
assert.throws(() => timerify(m, { histogram }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
@@ -132,17 +136,17 @@ const {
|
||||
|
||||
// Regression tests for https://github.com/nodejs/node/issues/40623
|
||||
{
|
||||
assert.strictEqual(performance.timerify(function func() {
|
||||
assert.strictEqual(timerify(function func() {
|
||||
return 1;
|
||||
})(), 1);
|
||||
assert.strictEqual(performance.timerify(function() {
|
||||
assert.strictEqual(timerify(function() {
|
||||
return 1;
|
||||
})(), 1);
|
||||
assert.strictEqual(performance.timerify(() => {
|
||||
assert.strictEqual(timerify(() => {
|
||||
return 1;
|
||||
})(), 1);
|
||||
class C {}
|
||||
const wrap = performance.timerify(C);
|
||||
const wrap = timerify(C);
|
||||
assert.ok(new wrap() instanceof C);
|
||||
assert.throws(() => wrap(), {
|
||||
name: 'TypeError',
|
||||
@@ -10,7 +10,7 @@ const {
|
||||
parentPort,
|
||||
} = require('worker_threads');
|
||||
const { performance } = require('perf_hooks');
|
||||
const { eventLoopUtilization } = require('perf_hooks').performance;
|
||||
const { eventLoopUtilization } = require('perf_hooks');
|
||||
|
||||
// Use argv to detect whether we're running as a Worker called by this test vs.
|
||||
// this test also being called as a Worker.
|
||||
|
||||
Reference in new issue
Block a user