From 723caba68514a0afdbe75077edadcc5ca271c0ce Mon Sep 17 00:00:00 2001 From: Luan Muniz Date: Tue, 23 Jun 2026 19:36:52 +0200 Subject: [PATCH] Add benchmarks for node only mode and mock timers. The only benchmark covers creating selected and non-selected tests when running with --test-only. The mock timers benchmark covers enabling timer mocks, setTimeout, setInterval, setImmediate, scheduler.wait, AbortSignal.timeout, mocked Date.now(), setTime(), and runAll(). Refs: https://github.com/nodejs/node/issues/55723 Signed-off-by: Luan Muniz --- benchmark/test_runner/mock-timers.js | 262 +++++++++++++++++++++++++++ benchmark/test_runner/test-only.js | 44 +++++ 2 files changed, 306 insertions(+) create mode 100644 benchmark/test_runner/mock-timers.js create mode 100644 benchmark/test_runner/test-only.js diff --git a/benchmark/test_runner/mock-timers.js b/benchmark/test_runner/mock-timers.js new file mode 100644 index 00000000000000..98b6d38e29c76f --- /dev/null +++ b/benchmark/test_runner/mock-timers.js @@ -0,0 +1,262 @@ +'use strict'; + +const common = require('../common'); +const assert = require('node:assert'); +const { test } = require('node:test'); +const nodeTimersPromises = require('node:timers/promises'); + +const bench = common.createBenchmark(main, { + n: [1, 10, 100, 1000], + mode: [ + 'enable-empty-apis', + 'enable-setTimeout', + 'enable-setInterval', + 'enable-setImmediate', + 'enable-Date', + 'enable-scheduler.wait', + 'enable-AbortSignal.timeout', + 'enable-all', + 'enable-default', + 'setTimeout', + 'setInterval', + 'setImmediate', + 'scheduler.wait', + 'AbortSignal.timeout', + 'Date', + 'setTime', + 'runAll', + ], +}, { + // We don't want to test the reporter here. + flags: ['--test-reporter=./benchmark/fixtures/empty-test-reporter.js'], +}); + +function benchmarkEnable(n, mode) { + const enableMode = mode.replace('enable-', ''); + let enableOptions = { apis: [enableMode] }; + + if (enableMode === 'all') { + enableOptions.apis = ['setTimeout', 'setInterval', 'setImmediate', 'Date', 'scheduler.wait', 'AbortSignal.timeout']; + } + + if (enableMode === 'empty-apis') { + enableOptions.apis = []; + } + + if (enableMode === 'default') { + enableOptions = undefined; + } + + test((t) => { + bench.start(); + + for (let i = 0; i < n; i++) { + t.mock.timers.enable(enableOptions); + t.mock.timers.reset(); + } + + bench.end(n); + }); +} + +function benchmarkSetTimeout(n) { + test((t) => { + let noDead; + + t.mock.timers.enable({ apis: ['setTimeout'] }); + bench.start(); + + for (let i = 0; i < n; i++) { + setTimeout(() => { + noDead = i; + }, i + 1); + } + + t.mock.timers.tick(n + 1); + bench.end(n); + + assert.strictEqual(noDead, n - 1); + }); +} + +function benchmarkSetInterval(n) { + test((t) => { + let noDead = 0; + + t.mock.timers.enable({ apis: ['setInterval'] }); + + setInterval(() => { + noDead++; + }, 1); + + bench.start(); + + t.mock.timers.tick(n); + + bench.end(n); + + assert.strictEqual(noDead, n); + }); +} + +function benchmarkSetImmediate(n) { + test((t) => { + let noDead; + + t.mock.timers.enable({ apis: ['setImmediate'] }); + + bench.start(); + + for (let i = 0; i < n; i++) { + setImmediate(() => { + noDead = i; + }); + } + + t.mock.timers.tick(0); + bench.end(n); + + assert.strictEqual(noDead, n - 1); + }); +} + +function benchmarkSchedulerWait(n) { + test(async (t) => { + const promises = []; + let noDead; + + t.mock.timers.enable({ apis: ['scheduler.wait'] }); + + bench.start(); + + for (let i = 0; i < n; i++) { + promises.push(nodeTimersPromises.scheduler.wait(i + 1).then(() => { + noDead = i; + })); + } + + t.mock.timers.tick(n + 1); + await Promise.all(promises); + bench.end(n); + + assert.strictEqual(noDead, n - 1); + }); +} + +function benchmarkAbortSignalTimeout(n) { + test((t) => { + let noDead; + + t.mock.timers.enable({ apis: ['AbortSignal.timeout'] }); + + bench.start(); + + for (let i = 0; i < n; i++) { + noDead = AbortSignal.timeout(i + 1); + } + + t.mock.timers.tick(n + 1); + bench.end(n); + + assert.strictEqual(noDead.aborted, true); + }); +} + +function benchmarkDate(n) { + test((t) => { + let noDead; + + t.mock.timers.enable({ apis: ['Date'] }); + + bench.start(); + + for (let i = 0; i < n; i++) { + noDead = Date.now(); + } + + bench.end(n); + + assert.strictEqual(noDead, 0); + }); +} + +function benchmarkSetTime(n) { + test((t) => { + let noDead; + + t.mock.timers.enable({ apis: ['Date'] }); + + bench.start(); + + for (let i = 0; i < n; i++) { + t.mock.timers.setTime(i); + noDead = Date.now(); + } + + bench.end(n); + + assert.strictEqual(noDead, n - 1); + }); +} + +function benchmarkRunAll(n) { + test((t) => { + let noDead = 0; + + t.mock.timers.enable({ apis: ['setTimeout'] }); + + for (let i = 0; i < n; i++) { + setTimeout(() => { + noDead++; + }, i + 1); + } + + bench.start(); + + t.mock.timers.runAll(); + + bench.end(n); + + assert.strictEqual(noDead, n); + }); +} + +function main({ n, mode }) { + switch (mode) { + case 'enable-empty-apis': + case 'enable-setTimeout': + case 'enable-setInterval': + case 'enable-setImmediate': + case 'enable-Date': + case 'enable-scheduler.wait': + case 'enable-AbortSignal.timeout': + case 'enable-all': + case 'enable-default': + benchmarkEnable(n, mode); + break; + case 'setTimeout': + benchmarkSetTimeout(n); + break; + case 'setInterval': + benchmarkSetInterval(n); + break; + case 'setImmediate': + benchmarkSetImmediate(n); + break; + case 'scheduler.wait': + benchmarkSchedulerWait(n); + break; + case 'AbortSignal.timeout': + benchmarkAbortSignalTimeout(n); + break; + case 'Date': + benchmarkDate(n); + break; + case 'setTime': + benchmarkSetTime(n); + break; + case 'runAll': + benchmarkRunAll(n); + break; + } +} diff --git a/benchmark/test_runner/test-only.js b/benchmark/test_runner/test-only.js new file mode 100644 index 00000000000000..8b8a9cb5d28aff --- /dev/null +++ b/benchmark/test_runner/test-only.js @@ -0,0 +1,44 @@ +'use strict'; + +const common = require('../common'); +const { finished } = require('node:stream/promises'); +const reporter = require('../fixtures/empty-test-reporter'); +const { test } = require('node:test'); + +const bench = common.createBenchmark(main, { + n: [1, 10, 100, 1000], + selected: [1], +}, { + // We don't want to test the reporter here. + flags: [ + '--test-reporter=./benchmark/fixtures/empty-test-reporter.js', + '--test-only', + ], +}); + +async function run({ n, selected }) { + // eslint-disable-next-line no-unused-vars + let avoidV8Optimization; + + for (let i = 0; i < selected; i++) { + test(`selected-${i}`, { only: true }, () => { + avoidV8Optimization = i; + }); + } + + for (let i = 0; i < n; i++) { + test(`not-selected-${i}`, () => { + throw new Error(`This test ${i} should not run.`); + }); + } + + return finished(reporter); +} + +function main(params) { + bench.start(); + + run(params).then(() => { + bench.end(params.n); + }); +}