mirror of
https://github.com/nodejs/node.git
synced 2025-05-21 17:44:15 +00:00

* Update the user timing implementation to conform to User Timing Level 3. * Reimplement user timing and timerify with pure JavaScript implementations * Simplify the C++ implementation for gc and http2 perf * Runtime deprecate additional perf entry properties in favor of the standard detail argument * Disable the `buffered` option on PerformanceObserver, all entries are queued and dispatched on setImmediate. Only entries with active observers are buffered. * This does remove the user timing and timerify trace events. Because the trace_events are still considered experimental, those can be removed without a deprecation cycle. They are removed to improve performance and reduce complexity. Old: `perf_hooks/usertiming.js n=100000: 92,378.01249733355` New: perf_hooks/usertiming.js n=100000: 270,393.5280638482` PR-URL: https://github.com/nodejs/node/pull/37136 Refs: https://github.com/nodejs/diagnostics/issues/464 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
154 lines
3.7 KiB
JavaScript
154 lines
3.7 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ObjectKeys,
|
|
SafeMap,
|
|
} = primordials;
|
|
|
|
const {
|
|
InternalPerformanceEntry,
|
|
kReadOnlyAttributes,
|
|
now,
|
|
} = require('internal/perf/perf');
|
|
|
|
const { enqueue } = require('internal/perf/observe');
|
|
|
|
const nodeTiming = require('internal/perf/nodetiming');
|
|
|
|
const {
|
|
validateNumber,
|
|
validateObject,
|
|
validateString,
|
|
} = require('internal/validators');
|
|
|
|
const {
|
|
codes: {
|
|
ERR_INVALID_ARG_VALUE,
|
|
ERR_INVALID_PERFORMANCE_MARK,
|
|
ERR_PERFORMANCE_INVALID_TIMESTAMP,
|
|
ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS,
|
|
},
|
|
} = require('internal/errors');
|
|
|
|
const marks = new SafeMap();
|
|
const nodeTimingReadOnlyAttributes =
|
|
nodeTiming.constructor[kReadOnlyAttributes];
|
|
|
|
function getMark(name) {
|
|
if (name === undefined) return;
|
|
if (typeof name === 'number') {
|
|
if (name < 0)
|
|
throw new ERR_PERFORMANCE_INVALID_TIMESTAMP(name);
|
|
return name;
|
|
}
|
|
name = `${name}`;
|
|
if (nodeTimingReadOnlyAttributes.has(name))
|
|
return nodeTiming[name];
|
|
const ts = marks.get(name);
|
|
if (ts === undefined)
|
|
throw new ERR_INVALID_PERFORMANCE_MARK(name);
|
|
return ts;
|
|
}
|
|
|
|
class PerformanceMark extends InternalPerformanceEntry {
|
|
constructor(name, options = {}) {
|
|
name = `${name}`;
|
|
if (nodeTimingReadOnlyAttributes.has(name))
|
|
throw new ERR_INVALID_ARG_VALUE('name', name);
|
|
validateObject(options, 'options');
|
|
const {
|
|
detail,
|
|
startTime = now(),
|
|
} = options;
|
|
validateNumber(startTime, 'startTime');
|
|
if (startTime < 0)
|
|
throw new ERR_PERFORMANCE_INVALID_TIMESTAMP(startTime);
|
|
marks.set(name, startTime);
|
|
super(name, 'mark', startTime, 0, detail);
|
|
enqueue(this);
|
|
}
|
|
}
|
|
|
|
class PerformanceMeasure extends InternalPerformanceEntry {
|
|
constructor(name, start, duration, detail) {
|
|
super(name, 'measure', start, duration, detail);
|
|
enqueue(this);
|
|
}
|
|
}
|
|
|
|
function mark(name, options = {}) {
|
|
return new PerformanceMark(name, options);
|
|
}
|
|
|
|
function calculateStartDuration(startOrMeasureOptions, endMark) {
|
|
startOrMeasureOptions ??= 0;
|
|
let detail;
|
|
let start;
|
|
let end;
|
|
let duration;
|
|
if (typeof startOrMeasureOptions === 'object' &&
|
|
ObjectKeys(startOrMeasureOptions).length) {
|
|
({
|
|
start,
|
|
end,
|
|
duration,
|
|
detail,
|
|
} = startOrMeasureOptions);
|
|
if (endMark !== undefined) {
|
|
throw new ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS(
|
|
'endMark must not be specified');
|
|
}
|
|
if (start === undefined && end === undefined) {
|
|
throw new ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS(
|
|
'One of options.start or options.end is required');
|
|
}
|
|
if (start !== undefined && end !== undefined && duration !== undefined) {
|
|
throw new ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS(
|
|
'Must not have options.start, options.end, and ' +
|
|
'options.duration specified');
|
|
}
|
|
start = getMark(start);
|
|
duration = getMark(duration);
|
|
} else {
|
|
start = getMark(startOrMeasureOptions);
|
|
}
|
|
|
|
end = getMark(endMark || end) ??
|
|
((start !== undefined && duration !== undefined) ?
|
|
start + duration : now());
|
|
|
|
start ??= (duration !== undefined) ? end - duration : 0;
|
|
|
|
duration ??= end - start;
|
|
|
|
return { start, duration, detail };
|
|
}
|
|
|
|
function measure(name, startOrMeasureOptions, endMark) {
|
|
validateString(name, 'name');
|
|
const {
|
|
start,
|
|
duration,
|
|
detail
|
|
} = calculateStartDuration(startOrMeasureOptions, endMark);
|
|
return new PerformanceMeasure(name, start, duration, detail);
|
|
}
|
|
|
|
function clearMarks(name) {
|
|
if (name !== undefined) {
|
|
name = `${name}`;
|
|
if (nodeTimingReadOnlyAttributes.has(name))
|
|
throw new ERR_INVALID_ARG_VALUE('name', name);
|
|
marks.delete(name);
|
|
return;
|
|
}
|
|
marks.clear();
|
|
}
|
|
|
|
module.exports = {
|
|
PerformanceMark,
|
|
clearMarks,
|
|
mark,
|
|
measure,
|
|
};
|