node/test/parallel/test-perf-hooks-usertiming.js
James M Snell f3eb224c83
perf_hooks: complete overhaul of the implementation
* 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>
2021-02-22 08:46:11 -08:00

181 lines
4.5 KiB
JavaScript

'use strict';
const common = require('../common');
const assert = require('assert');
const {
PerformanceObserver,
PerformanceEntry,
PerformanceMark,
performance: {
nodeTiming,
mark,
measure,
clearMarks,
},
} = require('perf_hooks');
assert(PerformanceObserver);
assert(PerformanceEntry);
assert(PerformanceMark);
assert(mark);
assert(measure);
[undefined, 'a', 'null', 1, true].forEach((i) => {
const m = mark(i);
assert(m instanceof PerformanceEntry);
assert(m instanceof PerformanceMark);
assert.strictEqual(m.name, `${i}`);
assert.strictEqual(m.entryType, 'mark');
assert.strictEqual(typeof m.startTime, 'number');
assert.strictEqual(m.duration, 0);
assert.strictEqual(m.details, undefined);
});
clearMarks();
assert.throws(() => mark(Symbol('a')), {
message: /Cannot convert a Symbol value to a string/
});
[undefined, null, 1, 'any', {}, []].forEach((detail) => {
const m = mark('a', { detail });
assert.strictEqual(m.name, 'a');
assert.strictEqual(m.entryType, 'mark');
assert.strictEqual(m.detail, detail);
});
clearMarks();
{
const m = mark('a', { startTime: 1 });
assert.strictEqual(m.startTime, 1);
}
assert.throws(() => mark('a', { startTime: 'a' }), {
code: 'ERR_INVALID_ARG_TYPE'
});
clearMarks();
clearMarks(1);
clearMarks(null);
assert.throws(() => clearMarks(Symbol('foo')), {
message: /Cannot convert a Symbol value to a string/
});
{
mark('a', { startTime: 0 });
mark('b', { startTime: 10 });
{
const m3 = measure('foo', 'a', 'b');
assert.strictEqual(m3.name, 'foo');
assert.strictEqual(m3.entryType, 'measure');
assert.strictEqual(m3.startTime, 0);
assert.strictEqual(m3.duration, 10);
}
{
const m3 = measure('foo', 'a');
assert.strictEqual(m3.name, 'foo');
assert.strictEqual(m3.entryType, 'measure');
assert.strictEqual(m3.startTime, 0);
assert(m3.duration > 0); // Duration is non-deterministic here.
}
{
const m3 = measure('foo', { start: 'a' });
assert.strictEqual(m3.name, 'foo');
assert.strictEqual(m3.entryType, 'measure');
assert.strictEqual(m3.startTime, 0);
assert(m3.duration > 0); // Duration is non-deterministic here.
}
{
const m3 = measure('foo', { end: 'b' });
assert.strictEqual(m3.name, 'foo');
assert.strictEqual(m3.entryType, 'measure');
assert.strictEqual(m3.startTime, 0);
assert.strictEqual(m3.duration, 10);
}
{
const m3 = measure('foo', { duration: 11, end: 'b' });
assert.strictEqual(m3.name, 'foo');
assert.strictEqual(m3.entryType, 'measure');
assert.strictEqual(m3.startTime, -1);
assert.strictEqual(m3.duration, 11);
}
{
const m3 = measure('foo', { duration: 11, start: 'b' });
assert.strictEqual(m3.name, 'foo');
assert.strictEqual(m3.entryType, 'measure');
assert.strictEqual(m3.startTime, 10);
assert.strictEqual(m3.duration, 11);
}
{
const m3 = measure('foo', 'nodeStart');
assert.strictEqual(m3.name, 'foo');
assert.strictEqual(m3.entryType, 'measure');
assert.strictEqual(m3.startTime, nodeTiming.nodeStart);
assert(m3.duration > 0); // Duration is non-deterministic here.
}
{
const m3 = measure('foo', 'nodeStart', 'bootstrapComplete');
assert.strictEqual(m3.name, 'foo');
assert.strictEqual(m3.entryType, 'measure');
assert.strictEqual(m3.startTime, nodeTiming.nodeStart);
assert.strictEqual(
m3.duration,
nodeTiming.bootstrapComplete - nodeTiming.nodeStart);
}
{
const m3 = measure('foo', { start: 'nodeStart', duration: 10 });
assert.strictEqual(m3.name, 'foo');
assert.strictEqual(m3.entryType, 'measure');
assert.strictEqual(m3.startTime, nodeTiming.nodeStart);
assert.strictEqual(m3.duration, 10);
}
clearMarks();
}
{
const obs = new PerformanceObserver(common.mustCall((list) => {
{
const entries = list.getEntries();
assert.strictEqual(entries.length, 3);
}
{
const entries = list.getEntriesByType('mark');
assert.strictEqual(entries.length, 2);
}
{
const entries = list.getEntriesByType('measure');
assert.strictEqual(entries.length, 1);
}
{
const entries = list.getEntriesByName('a');
assert.strictEqual(entries.length, 1);
}
{
const entries = list.getEntriesByName('b');
assert.strictEqual(entries.length, 1);
}
{
const entries = list.getEntriesByName('a to b');
assert.strictEqual(entries.length, 1);
}
obs.disconnect();
}));
obs.observe({ entryTypes: ['mark', 'measure'] });
mark('a');
mark('b');
measure('a to b', 'a', 'b');
}