From 84d042c82608095669f67e9b78326d88a394299c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Thu, 5 Oct 2023 21:39:55 -0300 Subject: [PATCH 1/5] perf_hooks: reduce overhead of createHistogram --- benchmark/perf_hooks/histogram.js | 42 +++++++++++++++++++++++ lib/internal/histogram.js | 56 ++++++++++++++++++++----------- 2 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 benchmark/perf_hooks/histogram.js diff --git a/benchmark/perf_hooks/histogram.js b/benchmark/perf_hooks/histogram.js new file mode 100644 index 00000000000000..adecdb400b0e6a --- /dev/null +++ b/benchmark/perf_hooks/histogram.js @@ -0,0 +1,42 @@ +'use strict'; + +const assert = require('assert'); +const common = require('../common.js'); + +const { createHistogram } = require('perf_hooks'); + +const bench = common.createBenchmark(main, { + n: [1e5], + operation: ['creation', 'clone'], +}); + +let _histogram; + +function main({ n, operation }) { + switch (operation) { + case 'creation': { + bench.start(); + for (let i = 0; i < n; i++) + _histogram = createHistogram(); + bench.end(n); + + // Avoid V8 deadcode (elimination) + assert.ok(_histogram); + break; + } + case 'clone': { + 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); + break; + } + default: + throw new Error(`Unsupported operation ${operation}`); + } +} diff --git a/lib/internal/histogram.js b/lib/internal/histogram.js index 079fbce50d54e3..cb757313009dc0 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,7 +326,7 @@ class RecordableHistogram extends Histogram { } } -function internalHistogram(handle) { +function ClonedHistogram(handle) { return ReflectConstruct( function() { markTransferMode(this, true, false); @@ -326,18 +334,26 @@ function internalHistogram(handle) { this[kMap] = new SafeMap(); }, [], Histogram); } -internalHistogram.prototype[kDeserialize] = () => {}; -function internalRecordableHistogram(handle) { - return ReflectConstruct( - function() { - markTransferMode(this, true, false); - this[kHandle] = handle; - this[kMap] = new SafeMap(); - this[kRecordable] = true; - }, [], RecordableHistogram); +ClonedHistogram.prototype[kDeserialize] = () => { }; + +function ClonedRecordableHistogram(handle) { + const histogram = new RecordableHistogram(kSkipThrow); + + markTransferMode(histogram, true, false); + histogram[kRecordable] = true; + histogram[kMap] = new SafeMap(); + histogram[kHandle] = handle; + histogram.constructor = RecordableHistogram; + + return histogram; +} + +ClonedRecordableHistogram.prototype[kDeserialize] = () => { }; + +function createRecordableHistogram(handle) { + return new ClonedRecordableHistogram(handle); } -internalRecordableHistogram.prototype[kDeserialize] = () => {}; /** * @param {{ @@ -363,14 +379,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, From f0e81e03ce65d1e49eefe2bd34d32892b512648c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Tue, 17 Oct 2023 21:26:40 -0300 Subject: [PATCH 2/5] test: avoid v8 deadcode on performance function --- test/parallel/test-performance-function.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/parallel/test-performance-function.js b/test/parallel/test-performance-function.js index 5f774d6c2adcf5..6438dbf3c7abb7 100644 --- a/test/parallel/test-performance-function.js +++ b/test/parallel/test-performance-function.js @@ -90,8 +90,14 @@ 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; + }; + assert.ok(_deadCode >= 0) const n = performance.timerify(m, { histogram }); assert.strictEqual(histogram.max, 0); for (let i = 0; i < 10; i++) { From 8c2169f9ef70cd36414ddb36ad09196329fb7226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Tue, 17 Oct 2023 21:57:35 -0300 Subject: [PATCH 3/5] fixup! test: avoid v8 deadcode on performance function --- test/parallel/test-performance-function.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-performance-function.js b/test/parallel/test-performance-function.js index 6438dbf3c7abb7..c2dd65e527e0df 100644 --- a/test/parallel/test-performance-function.js +++ b/test/parallel/test-performance-function.js @@ -97,7 +97,7 @@ const { for (let i = 0; i < 1e3; i++) _deadCode = i; }; - assert.ok(_deadCode >= 0) + assert.ok(_deadCode >= 0); const n = performance.timerify(m, { histogram }); assert.strictEqual(histogram.max, 0); for (let i = 0; i < 10; i++) { From bab454c347573d984fdbf83ce8032433a4369bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Wed, 18 Oct 2023 18:03:57 -0300 Subject: [PATCH 4/5] fixup! test: avoid v8 deadcode on performance function --- test/parallel/test-performance-function.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-performance-function.js b/test/parallel/test-performance-function.js index c2dd65e527e0df..b69a21308cf048 100644 --- a/test/parallel/test-performance-function.js +++ b/test/parallel/test-performance-function.js @@ -97,13 +97,13 @@ const { for (let i = 0; i < 1e3; i++) _deadCode = i; }; - assert.ok(_deadCode >= 0); 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 }), { From 34c403e29546fb0ab4e482a9adc6602ea14af4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Wed, 18 Oct 2023 18:07:36 -0300 Subject: [PATCH 5/5] fixup! perf_hooks: reduce overhead of createHistogram --- benchmark/perf_hooks/histogram-clone.js | 24 ++++++++++++++ benchmark/perf_hooks/histogram-create.js | 22 +++++++++++++ benchmark/perf_hooks/histogram.js | 42 ------------------------ 3 files changed, 46 insertions(+), 42 deletions(-) create mode 100644 benchmark/perf_hooks/histogram-clone.js create mode 100644 benchmark/perf_hooks/histogram-create.js delete mode 100644 benchmark/perf_hooks/histogram.js 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/benchmark/perf_hooks/histogram.js b/benchmark/perf_hooks/histogram.js deleted file mode 100644 index adecdb400b0e6a..00000000000000 --- a/benchmark/perf_hooks/histogram.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -const assert = require('assert'); -const common = require('../common.js'); - -const { createHistogram } = require('perf_hooks'); - -const bench = common.createBenchmark(main, { - n: [1e5], - operation: ['creation', 'clone'], -}); - -let _histogram; - -function main({ n, operation }) { - switch (operation) { - case 'creation': { - bench.start(); - for (let i = 0; i < n; i++) - _histogram = createHistogram(); - bench.end(n); - - // Avoid V8 deadcode (elimination) - assert.ok(_histogram); - break; - } - case 'clone': { - 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); - break; - } - default: - throw new Error(`Unsupported operation ${operation}`); - } -}