mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
perf_hooks: add histogram option to timerify
Allows setting a `Histogram` object option on timerify to record function execution times over time. Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37475 Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
4 files changed
+82
-5
No files matched your search
@@ -210,10 +210,13 @@ added: v8.5.0
|
|||||||
The [`timeOrigin`][] specifies the high resolution millisecond timestamp at
|
The [`timeOrigin`][] specifies the high resolution millisecond timestamp at
|
||||||
which the current `node` process began, measured in Unix time.
|
which the current `node` process began, measured in Unix time.
|
||||||
|
|
||||||
### `performance.timerify(fn)`
|
### `performance.timerify(fn[, options])`
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.5.0
|
added: v8.5.0
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/37475
|
||||||
|
description: Added the histogram option.
|
||||||
- version: REPLACEME
|
- version: REPLACEME
|
||||||
pr-url: https://github.com/nodejs/node/pull/37136
|
pr-url: https://github.com/nodejs/node/pull/37136
|
||||||
description: Re-implemented to use pure-JavaScript and the ability
|
description: Re-implemented to use pure-JavaScript and the ability
|
||||||
@@ -221,6 +224,10 @@ changes:
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
* `fn` {Function}
|
* `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._
|
_This property is an extension by Node.js. It is not available in Web browsers._
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ const {
|
|||||||
JSTransferable,
|
JSTransferable,
|
||||||
} = require('internal/worker/js_transferable');
|
} = require('internal/worker/js_transferable');
|
||||||
|
|
||||||
|
function isHistogram(object) {
|
||||||
|
return object?.[kHandle] !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
class Histogram extends JSTransferable {
|
class Histogram extends JSTransferable {
|
||||||
constructor(internal) {
|
constructor(internal) {
|
||||||
super();
|
super();
|
||||||
@@ -193,6 +197,7 @@ module.exports = {
|
|||||||
RecordableHistogram,
|
RecordableHistogram,
|
||||||
InternalHistogram,
|
InternalHistogram,
|
||||||
InternalRecordableHistogram,
|
InternalRecordableHistogram,
|
||||||
|
isHistogram,
|
||||||
kDestroy,
|
kDestroy,
|
||||||
kHandle,
|
kHandle,
|
||||||
createHistogram,
|
createHistogram,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
const {
|
const {
|
||||||
FunctionPrototypeBind,
|
FunctionPrototypeBind,
|
||||||
ObjectDefineProperties,
|
ObjectDefineProperties,
|
||||||
|
MathCeil,
|
||||||
ReflectApply,
|
ReflectApply,
|
||||||
ReflectConstruct,
|
ReflectConstruct,
|
||||||
Symbol,
|
Symbol,
|
||||||
@@ -13,6 +14,14 @@ const {
|
|||||||
now,
|
now,
|
||||||
} = require('internal/perf/perf');
|
} = require('internal/perf/perf');
|
||||||
|
|
||||||
|
const {
|
||||||
|
validateObject
|
||||||
|
} = require('internal/validators');
|
||||||
|
|
||||||
|
const {
|
||||||
|
isHistogram
|
||||||
|
} = require('internal/histogram');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isConstructor,
|
isConstructor,
|
||||||
} = internalBinding('util');
|
} = internalBinding('util');
|
||||||
@@ -29,8 +38,10 @@ const {
|
|||||||
|
|
||||||
const kTimerified = Symbol('kTimerified');
|
const kTimerified = Symbol('kTimerified');
|
||||||
|
|
||||||
function processComplete(name, start, args) {
|
function processComplete(name, start, args, histogram) {
|
||||||
const duration = now() - start;
|
const duration = now() - start;
|
||||||
|
if (histogram !== undefined)
|
||||||
|
histogram.record(MathCeil(duration * 1e6));
|
||||||
const entry =
|
const entry =
|
||||||
new InternalPerformanceEntry(
|
new InternalPerformanceEntry(
|
||||||
name,
|
name,
|
||||||
@@ -45,10 +56,23 @@ function processComplete(name, start, args) {
|
|||||||
enqueue(entry);
|
enqueue(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
function timerify(fn) {
|
function timerify(fn, options = {}) {
|
||||||
if (typeof fn !== 'function')
|
if (typeof fn !== 'function')
|
||||||
throw new ERR_INVALID_ARG_TYPE('fn', 'function', fn);
|
throw new ERR_INVALID_ARG_TYPE('fn', 'function', fn);
|
||||||
|
|
||||||
|
validateObject(options, 'options');
|
||||||
|
const {
|
||||||
|
histogram
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
if (histogram !== undefined &&
|
||||||
|
(!isHistogram(histogram) || typeof histogram.record !== 'function')) {
|
||||||
|
throw new ERR_INVALID_ARG_TYPE(
|
||||||
|
'options.histogram',
|
||||||
|
'RecordableHistogram',
|
||||||
|
histogram);
|
||||||
|
}
|
||||||
|
|
||||||
if (fn[kTimerified]) return fn[kTimerified];
|
if (fn[kTimerified]) return fn[kTimerified];
|
||||||
|
|
||||||
const constructor = isConstructor(fn);
|
const constructor = isConstructor(fn);
|
||||||
@@ -65,9 +89,10 @@ function timerify(fn) {
|
|||||||
result,
|
result,
|
||||||
fn.name,
|
fn.name,
|
||||||
start,
|
start,
|
||||||
args));
|
args,
|
||||||
|
histogram));
|
||||||
}
|
}
|
||||||
processComplete(fn.name, start, args);
|
processComplete(fn.name, start, args, histogram);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,15 @@ const common = require('../common');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
createHistogram,
|
||||||
performance,
|
performance,
|
||||||
PerformanceObserver
|
PerformanceObserver
|
||||||
} = require('perf_hooks');
|
} = require('perf_hooks');
|
||||||
|
|
||||||
|
const {
|
||||||
|
setTimeout: sleep
|
||||||
|
} = require('timers/promises');
|
||||||
|
|
||||||
{
|
{
|
||||||
// Intentional non-op. Do not wrap in common.mustCall();
|
// Intentional non-op. Do not wrap in common.mustCall();
|
||||||
const n = performance.timerify(function noop() {});
|
const n = performance.timerify(function noop() {});
|
||||||
@@ -81,3 +86,38 @@ const {
|
|||||||
assert.strictEqual(n.length, m.length);
|
assert.strictEqual(n.length, m.length);
|
||||||
assert.strictEqual(n.name, 'timerified m');
|
assert.strictEqual(n.name, 'timerified m');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const histogram = createHistogram();
|
||||||
|
const m = (a, b = 1) => {};
|
||||||
|
const n = performance.timerify(m, { histogram });
|
||||||
|
assert.strictEqual(histogram.max, 0);
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
n();
|
||||||
|
await sleep(10);
|
||||||
|
}
|
||||||
|
assert.notStrictEqual(histogram.max, 0);
|
||||||
|
[1, '', {}, [], false].forEach((histogram) => {
|
||||||
|
assert.throws(() => performance.timerify(m, { histogram }), {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})().then(common.mustCall());
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const histogram = createHistogram();
|
||||||
|
const m = async (a, b = 1) => {
|
||||||
|
await sleep(10);
|
||||||
|
};
|
||||||
|
const n = performance.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 }), {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})().then(common.mustCall());
|
||||||
Reference in new issue
Block a user