@jonahsnider/benchmark
Version:
A Node.js benchmarking library with support for multithreading and TurboFan optimization isolation.
157 lines • 6.14 kB
JavaScript
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