From a517be0a5a532dfae3d8deaefeeb39c0569e3259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vinicius=20Louren=C3=A7o?= <12551007+H4ad@users.noreply.github.com> Date: Tue, 3 Oct 2023 21:38:32 -0300 Subject: [PATCH] perf_hooks: reducing overhead of performance observer entry list PR-URL: https://github.com/nodejs/node/pull/50008 Reviewed-By: Stephen Belanger Reviewed-By: Yagiz Nizipli --- benchmark/perf_hooks/performance-observer.js | 49 ++++++++++++++++++++ lib/internal/perf/observe.js | 31 ++++++------- 2 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 benchmark/perf_hooks/performance-observer.js diff --git a/benchmark/perf_hooks/performance-observer.js b/benchmark/perf_hooks/performance-observer.js new file mode 100644 index 00000000000000..42a94932860c38 --- /dev/null +++ b/benchmark/perf_hooks/performance-observer.js @@ -0,0 +1,49 @@ +'use strict'; + +const assert = require('assert'); +const common = require('../common.js'); + +const { + PerformanceObserver, + performance, +} = require('perf_hooks'); + +function randomFn() { + return Math.random(); +} + +const bench = common.createBenchmark(main, { + n: [1e5], + pending: [1, 10], +}, { + options: ['--expose-internals'], +}); + +let _result; + +function fillQueue(timerfied, pending) { + for (let i = 0; i < pending; i++) { + _result = timerfied(); + } + // Avoid V8 deadcode (elimination) + assert.ok(_result); +} + +function main({ n, pending }) { + const timerfied = performance.timerify(randomFn); + + let count = 0; + const obs = new PerformanceObserver((entries) => { + count += entries.getEntries().length; + + if (count >= n) { + bench.end(count); + } else { + fillQueue(timerfied, pending); + } + }); + obs.observe({ entryTypes: ['function'], buffered: true }); + + bench.start(); + fillQueue(timerfied, pending); +} diff --git a/lib/internal/perf/observe.js b/lib/internal/perf/observe.js index b97ae08402260a..244d01f541d886 100644 --- a/lib/internal/perf/observe.js +++ b/lib/internal/perf/observe.js @@ -15,7 +15,6 @@ const { ObjectDefineProperties, ObjectFreeze, ObjectKeys, - ReflectConstruct, SafeMap, SafeSet, Symbol, @@ -171,9 +170,18 @@ function maybeIncrementObserverCount(type) { } } +const kSkipThrow = Symbol('kSkipThrow'); +const performanceObserverSorter = (first, second) => { + return first.startTime - second.startTime; +}; + class PerformanceObserverEntryList { - constructor() { - throw new ERR_ILLEGAL_CONSTRUCTOR(); + constructor(skipThrowSymbol = undefined, entries = []) { + if (skipThrowSymbol !== kSkipThrow) { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } + + this[kBuffer] = ArrayPrototypeSort(entries, performanceObserverSorter); } getEntries() { @@ -232,14 +240,6 @@ ObjectDefineProperties(PerformanceObserverEntryList.prototype, { }, }); -function createPerformanceObserverEntryList(entries) { - return ReflectConstruct(function PerformanceObserverEntryList() { - this[kBuffer] = ArrayPrototypeSort(entries, (first, second) => { - return first.startTime - second.startTime; - }); - }, [], PerformanceObserverEntryList); -} - class PerformanceObserver { #buffer = []; #entryTypes = new SafeSet(); @@ -349,8 +349,9 @@ class PerformanceObserver { } [kDispatch]() { - this.#callback(createPerformanceObserverEntryList(this.takeRecords()), - this); + const entryList = new PerformanceObserverEntryList(kSkipThrow, this.takeRecords()); + + this.#callback(entryList, this); } [kInspect](depth, options) { @@ -523,9 +524,7 @@ function filterBufferMapByNameAndType(name, type) { bufferList = ArrayPrototypeSlice(bufferList); } - return ArrayPrototypeSort(bufferList, (first, second) => { - return first.startTime - second.startTime; - }); + return ArrayPrototypeSort(bufferList, performanceObserverSorter); } function observerCallback(name, type, startTime, duration, details) {