diff --git a/benchmark/perf_hooks/histogram-clone.js b/benchmark/perf_hooks/histogram-clone.js new file mode 100644 index 00000000000000..4b610963fa2a0a --- /dev/null +++ b/benchmark/perf_hooks/histogram-clone.js @@ -0,0 +1,24 @@ +'use strict'; + +const assert = require('assert'); +const common = require('../common.js'); + +const { createHistogram } = require('perf_hooks'); + +const bench = common.createBenchmark(main, { + n: [1e5], +}); + +let _histogram; + +function main({ n }) { + const histogram = createHistogram(); + + bench.start(); + for (let i = 0; i < n; i++) + _histogram = structuredClone(histogram); + bench.end(n); + + // Avoid V8 deadcode (elimination) + assert.ok(_histogram); +} diff --git a/benchmark/perf_hooks/histogram-create.js b/benchmark/perf_hooks/histogram-create.js new file mode 100644 index 00000000000000..89ddad1fa79224 --- /dev/null +++ b/benchmark/perf_hooks/histogram-create.js @@ -0,0 +1,22 @@ +'use strict'; + +const assert = require('assert'); +const common = require('../common.js'); + +const { createHistogram } = require('perf_hooks'); + +const bench = common.createBenchmark(main, { + n: [1e5], +}); + +let _histogram; + +function main({ n }) { + bench.start(); + for (let i = 0; i < n; i++) + _histogram = createHistogram(); + bench.end(n); + + // Avoid V8 deadcode (elimination) + assert.ok(_histogram); +} diff --git a/lib/internal/histogram.js b/lib/internal/histogram.js index 37bde8195e4d34..62d72e7c99475c 100644 --- a/lib/internal/histogram.js +++ b/lib/internal/histogram.js @@ -52,9 +52,13 @@ function isHistogram(object) { return object?.[kHandle] !== undefined; } +const kSkipThrow = Symbol('kSkipThrow'); + class Histogram { - constructor() { - throw new ERR_ILLEGAL_CONSTRUCTOR(); + constructor(skipThrowSymbol = undefined) { + if (skipThrowSymbol !== kSkipThrow) { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } } [kInspect](depth, options) { @@ -242,7 +246,7 @@ class Histogram { const handle = this[kHandle]; return { data: { handle }, - deserializeInfo: 'internal/histogram:internalHistogram', + deserializeInfo: 'internal/histogram:ClonedHistogram', }; } @@ -264,8 +268,12 @@ class Histogram { } class RecordableHistogram extends Histogram { - constructor() { - throw new ERR_ILLEGAL_CONSTRUCTOR(); + constructor(skipThrowSymbol = undefined) { + if (skipThrowSymbol !== kSkipThrow) { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } + + super(skipThrowSymbol); } /** @@ -309,7 +317,7 @@ class RecordableHistogram extends Histogram { const handle = this[kHandle]; return { data: { handle }, - deserializeInfo: 'internal/histogram:internalRecordableHistogram', + deserializeInfo: 'internal/histogram:ClonedRecordableHistogram', }; } @@ -318,24 +326,32 @@ class RecordableHistogram extends Histogram { } } -function internalHistogram(handle) { +function ClonedHistogram(handle) { return makeTransferable(ReflectConstruct( function() { this[kHandle] = handle; this[kMap] = new SafeMap(); }, [], Histogram)); } -internalHistogram.prototype[kDeserialize] = () => {}; -function internalRecordableHistogram(handle) { - return makeTransferable(ReflectConstruct( - function() { - this[kHandle] = handle; - this[kMap] = new SafeMap(); - this[kRecordable] = true; - }, [], RecordableHistogram)); +ClonedHistogram.prototype[kDeserialize] = () => { }; + +function ClonedRecordableHistogram(handle) { + const histogram = new RecordableHistogram(kSkipThrow); + + histogram[kRecordable] = true; + histogram[kMap] = new SafeMap(); + histogram[kHandle] = handle; + histogram.constructor = RecordableHistogram; + + return makeTransferable(histogram); +} + +ClonedRecordableHistogram.prototype[kDeserialize] = () => { }; + +function createRecordableHistogram(handle) { + return new ClonedRecordableHistogram(handle); } -internalRecordableHistogram.prototype[kDeserialize] = () => {}; /** * @param {{ @@ -361,14 +377,14 @@ function createHistogram(options = kEmptyObject) { throw new ERR_INVALID_ARG_VALUE.RangeError('options.highest', highest); } validateInteger(figures, 'options.figures', 1, 5); - return internalRecordableHistogram(new _Histogram(lowest, highest, figures)); + return createRecordableHistogram(new _Histogram(lowest, highest, figures)); } module.exports = { Histogram, RecordableHistogram, - internalHistogram, - internalRecordableHistogram, + ClonedHistogram, + ClonedRecordableHistogram, isHistogram, kDestroy, kHandle, diff --git a/test/parallel/test-performance-function.js b/test/parallel/test-performance-function.js index 5f774d6c2adcf5..b69a21308cf048 100644 --- a/test/parallel/test-performance-function.js +++ b/test/parallel/test-performance-function.js @@ -90,14 +90,20 @@ const { } (async () => { + let _deadCode; + const histogram = createHistogram(); - const m = (a, b = 1) => {}; + const m = (a, b = 1) => { + for (let i = 0; i < 1e3; i++) + _deadCode = i; + }; const n = performance.timerify(m, { histogram }); assert.strictEqual(histogram.max, 0); for (let i = 0; i < 10; i++) { n(); await sleep(10); } + assert.ok(_deadCode >= 0); assert.notStrictEqual(histogram.max, 0); [1, '', {}, [], false].forEach((histogram) => { assert.throws(() => performance.timerify(m, { histogram }), {