From 5fd5fadc3e76f0058df6f5395a5f57ceabee2384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Wed, 27 Sep 2023 22:15:16 -0300 Subject: [PATCH 1/2] perf_hooks: reduce overhead of new user timings --- lib/internal/perf/performance_entry.js | 13 ++----- lib/internal/perf/usertiming.js | 53 ++++++++++++++++---------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/lib/internal/perf/performance_entry.js b/lib/internal/perf/performance_entry.js index f6a7520e267c5b..76af221a5d1ae2 100644 --- a/lib/internal/perf/performance_entry.js +++ b/lib/internal/perf/performance_entry.js @@ -42,7 +42,10 @@ class PerformanceEntry { throw new ERR_ILLEGAL_CONSTRUCTOR(); } - initPerformanceEntry(this, name, type, start, duration); + this[kName] = name; + this[kEntryType] = type; + this[kStartTime] = start; + this[kDuration] = duration; } get name() { @@ -94,13 +97,6 @@ ObjectDefineProperties(PerformanceEntry.prototype, { toJSON: kEnumerableProperty, }); -function initPerformanceEntry(entry, name, type, start, duration) { - entry[kName] = name; - entry[kEntryType] = type; - entry[kStartTime] = start; - entry[kDuration] = duration; -} - function createPerformanceEntry(name, type, start, duration) { return new PerformanceEntry(kSkipThrow, name, type, start, duration); } @@ -135,7 +131,6 @@ function createPerformanceNodeEntry(name, type, start, duration, detail) { } module.exports = { - initPerformanceEntry, createPerformanceEntry, PerformanceEntry, isPerformanceEntry, diff --git a/lib/internal/perf/usertiming.js b/lib/internal/perf/usertiming.js index 42c343a632be40..63f4fa0e8874a0 100644 --- a/lib/internal/perf/usertiming.js +++ b/lib/internal/perf/usertiming.js @@ -2,16 +2,14 @@ const { ObjectDefineProperties, - ObjectSetPrototypeOf, SafeMap, SafeSet, SafeArrayIterator, Symbol, SymbolToStringTag, - ReflectConstruct, } = primordials; -const { initPerformanceEntry, PerformanceEntry } = require('internal/perf/performance_entry'); +const { PerformanceEntry, kSkipThrow } = require('internal/perf/performance_entry'); const { now } = require('internal/perf/utils'); const { enqueue, bufferUserTiming } = require('internal/perf/observe'); const nodeTiming = require('internal/perf/nodetiming'); @@ -35,7 +33,6 @@ const { const { structuredClone } = require('internal/structured_clone'); const { - kEmptyObject, lazyDOMException, kEnumerableProperty, } = require('internal/util'); @@ -69,27 +66,33 @@ function getMark(name) { return ts; } -class PerformanceMark { - constructor(name, options = kEmptyObject) { - if (arguments.length === 0) { +const kEmptyArg = Symbol('kEmptyArg'); + +class PerformanceMark extends PerformanceEntry { + constructor(name = kEmptyArg, options = undefined) { + if (name === kEmptyArg) { throw new ERR_MISSING_ARGS('name'); } name = `${name}`; - options ??= kEmptyObject; if (nodeTimingReadOnlyAttributes.has(name)) throw new ERR_INVALID_ARG_VALUE('name', name); - validateObject(options, 'options'); - const startTime = options.startTime ?? now(); + if (options !== undefined) { + validateObject(options, 'options'); + } + const startTime = options?.startTime ?? now(); validateNumber(startTime, 'startTime'); if (startTime < 0) throw new ERR_PERFORMANCE_INVALID_TIMESTAMP(startTime); markTimings.set(name, startTime); - let detail = options.detail; + let detail = options?.detail; + // The usage of != is intentional, we want to skip structuredClone + // for both undefined and null detail = detail != null ? structuredClone(detail) : null; - initPerformanceEntry(this, name, 'mark', startTime, 0); + + super(kSkipThrow, name, 'mark', startTime, 0); this[kDetail] = detail; } @@ -108,8 +111,7 @@ class PerformanceMark { }; } } -ObjectSetPrototypeOf(PerformanceMark, PerformanceEntry); -ObjectSetPrototypeOf(PerformanceMark.prototype, PerformanceEntry.prototype); + ObjectDefineProperties(PerformanceMark.prototype, { detail: kEnumerableProperty, [SymbolToStringTag]: { @@ -120,8 +122,18 @@ ObjectDefineProperties(PerformanceMark.prototype, { }); class PerformanceMeasure extends PerformanceEntry { - constructor() { - throw new ERR_ILLEGAL_CONSTRUCTOR(); + constructor( + skipThrowSymbol = undefined, + name = undefined, + type = undefined, + start = undefined, + duration = undefined, + ) { + if (skipThrowSymbol !== kSkipThrow) { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } + + super(skipThrowSymbol, name, type, start, duration); } get detail() { @@ -139,10 +151,11 @@ ObjectDefineProperties(PerformanceMeasure.prototype, { }); function createPerformanceMeasure(name, start, duration, detail) { - return ReflectConstruct(function PerformanceMeasure() { - initPerformanceEntry(this, name, 'measure', start, duration); - this[kDetail] = detail; - }, [], PerformanceMeasure); + const measure = new PerformanceMeasure(kSkipThrow, name, 'measure', start, duration); + + measure[kDetail] = detail; + + return measure; } function mark(name, options) { From 41a0fdf233e1812fdf0ccfaebb6789f6e168be91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Sun, 1 Oct 2023 13:16:11 -0300 Subject: [PATCH 2/2] fixup! perf_hooks: reduce overhead of new user timings --- lib/internal/perf/usertiming.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/internal/perf/usertiming.js b/lib/internal/perf/usertiming.js index 63f4fa0e8874a0..ff417d94700550 100644 --- a/lib/internal/perf/usertiming.js +++ b/lib/internal/perf/usertiming.js @@ -66,17 +66,15 @@ function getMark(name) { return ts; } -const kEmptyArg = Symbol('kEmptyArg'); - class PerformanceMark extends PerformanceEntry { - constructor(name = kEmptyArg, options = undefined) { - if (name === kEmptyArg) { + constructor(name, options = undefined) { + if (arguments.length === 0) { throw new ERR_MISSING_ARGS('name'); } name = `${name}`; if (nodeTimingReadOnlyAttributes.has(name)) throw new ERR_INVALID_ARG_VALUE('name', name); - if (options !== undefined) { + if (options != null) { validateObject(options, 'options'); } const startTime = options?.startTime ?? now(); @@ -86,8 +84,6 @@ class PerformanceMark extends PerformanceEntry { markTimings.set(name, startTime); let detail = options?.detail; - // The usage of != is intentional, we want to skip structuredClone - // for both undefined and null detail = detail != null ? structuredClone(detail) : null;