From 19635114afb144efaf6e72d57e6894d9e4eb453e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= <contact@viniciusl.com.br>
Date: Thu, 5 Oct 2023 21:39:55 -0300
Subject: [PATCH 1/2] perf_hooks: reduce overhead of createHistogram

PR-URL: https://github.com/nodejs/node/pull/50074
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>

Backport-PR-URL: https://github.com/nodejs/node/pull/50074
---
 benchmark/perf_hooks/histogram-clone.js  | 24 +++++++++++
 benchmark/perf_hooks/histogram-create.js | 22 ++++++++++
 lib/internal/histogram.js                | 54 +++++++++++++++---------
 3 files changed, 81 insertions(+), 19 deletions(-)
 create mode 100644 benchmark/perf_hooks/histogram-clone.js
 create mode 100644 benchmark/perf_hooks/histogram-create.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/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,

From e89f7027a5a31768523f9e6e9f6e7fa179166def Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= <contact@viniciusl.com.br>
Date: Tue, 17 Oct 2023 21:26:40 -0300
Subject: [PATCH 2/2] test: avoid v8 deadcode on performance function

PR-URL: https://github.com/nodejs/node/pull/50074
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>

Backport-PR-URL: https://github.com/nodejs/node/pull/50074
---
 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..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 }), {