UNPKG

@jonahsnider/benchmark

Version:

A Node.js benchmarking library with support for multithreading and TurboFan optimization isolation.

157 lines 6.14 kB
var _Suite_instances, _Suite_tests, _Suite_clearResults, _Suite_runTestsOnce, _Suite_runTestsWithOptions, _Suite_runTests, _Suite_runWarmup; import { __classPrivateFieldGet } from "tslib"; import assert from 'node:assert/strict'; import { performance } from 'node:perf_hooks'; import { Test } from './test.js'; import { AbortError } from './utils.js'; /** * A collection of {@link (Test:class)}s that are different implementations of the same thing (ex. different ways of sorting an array). * * @example * ```js * import { Suite } from '@jonahsnider/benchmark'; * * const suite = new Suite('concatenation', { warmup: { durationMs: 10_000 }, run: { durationMs: 10_000 } }) * .addTest('+', () => 'a' + 'b') * .addTest('templates', () => `${'a'}${'b'}`) * .addTest('.concat()', () => 'a'.concat('b')); * * const results = await suite.run(); * * console.log(results); * ``` * * @public */ export class Suite { /** * This {@link (Suite:class)}'s filepath, if it was provided. * Used for running the {@link (Suite:class)} in a separate thread. */ get filepath() { return this.options.filepath; } /** * Creates a new {@link (Suite:class)}. * * @example * ```js * import { Suite } from '@jonahsnider/benchmark'; * * const suite = new Suite('concatenation', { warmup: { durationMs: 10_000 }, run: { durationMs: 10_000 } }); * ``` * * @example * Suites that specify a filepath can be run in a separate thread in a {@link (Benchmark:class)}. * ```js * import { Suite } from '@jonahsnider/benchmark'; * * const suite = new Suite('concatenation', { * warmup: { durationMs: 10_000 }, * run: { durationMs: 10_000 }, * filepath: import.meta.url * }); * ``` * * @param name - The name of the {@link (Suite:class)} * @param options - Options for the {@link (Suite:class)} */ constructor(name, /** * Options for running this {@link (Suite:class)} and its warmup. */ options) { _Suite_instances.add(this); this.name = name; this.options = options; _Suite_tests.set(this, new Map()); /** * The tests in this {@link (Suite:class)}. */ // eslint-disable-next-line @typescript-eslint/member-ordering this.tests = __classPrivateFieldGet(this, _Suite_tests, "f"); } addTest(testName, fnOrTest) { assert.ok(!__classPrivateFieldGet(this, _Suite_tests, "f").has(testName)); assert.strictEqual(typeof testName, 'string', new TypeError(`The "testName" argument must be of type string.`)); if (fnOrTest instanceof Test) { __classPrivateFieldGet(this, _Suite_tests, "f").set(testName, fnOrTest); } else { assert.strictEqual(typeof fnOrTest, 'function', new TypeError(`The "fn" argument must be of type function.`)); __classPrivateFieldGet(this, _Suite_tests, "f").set(testName, new Test(fnOrTest)); } return this; } /** * Runs this {@link (Suite:class)} using {@link (Suite:class).options}. * * @example * ```js * const results = await suite.run(); * ``` * * @example * Using an `AbortSignal` to cancel the suite: * ```js * const ac = new AbortController(); * const signal = ac.signal; * * suite * .run(signal) * .then(console.log) * .catch(error => { * if (error.name === 'AbortError') { * console.log('The suite was aborted'); * } * }); * * ac.abort(); * ``` * * @returns The results of running this {@link (Suite:class)} */ async run(abortSignal) { __classPrivateFieldGet(this, _Suite_instances, "m", _Suite_clearResults).call(this); await __classPrivateFieldGet(this, _Suite_instances, "m", _Suite_runWarmup).call(this, abortSignal); await __classPrivateFieldGet(this, _Suite_instances, "m", _Suite_runTests).call(this, abortSignal); const results = new Map([...__classPrivateFieldGet(this, _Suite_tests, "f").entries()].map(([testName, test]) => [testName, test.histogram])); return results; } } _Suite_tests = new WeakMap(), _Suite_instances = new WeakSet(), _Suite_clearResults = function _Suite_clearResults() { for (const tests of __classPrivateFieldGet(this, _Suite_tests, "f").values()) { tests.histogram.reset(); } }, _Suite_runTestsOnce = async function _Suite_runTestsOnce() { for (const test of __classPrivateFieldGet(this, _Suite_tests, "f").values()) { // eslint-disable-next-line no-await-in-loop await test.run(); } }, _Suite_runTestsWithOptions = async function _Suite_runTestsWithOptions(options, abortSignal) { if (options.durationMs === undefined) { for (let count = 0; count < options.trials; count++) { if (abortSignal?.aborted) { throw new AbortError(); } // eslint-disable-next-line no-await-in-loop await __classPrivateFieldGet(this, _Suite_instances, "m", _Suite_runTestsOnce).call(this); } } else { const startTime = performance.now(); while (performance.now() - startTime < options.durationMs) { if (abortSignal?.aborted) { throw new AbortError(); } // eslint-disable-next-line no-await-in-loop await __classPrivateFieldGet(this, _Suite_instances, "m", _Suite_runTestsOnce).call(this); } } }, _Suite_runTests = async function _Suite_runTests(abortSignal) { await __classPrivateFieldGet(this, _Suite_instances, "m", _Suite_runTestsWithOptions).call(this, this.options.run, abortSignal); }, _Suite_runWarmup = async function _Suite_runWarmup(abortSignal) { await __classPrivateFieldGet(this, _Suite_instances, "m", _Suite_runTestsWithOptions).call(this, this.options.warmup, abortSignal); __classPrivateFieldGet(this, _Suite_instances, "m", _Suite_clearResults).call(this); }; //# sourceMappingURL=suite.js.map