Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ A `Suite` manages and executes benchmark functions. It provides two methods: `ad
* `'time'` - Measures actual execution time for a single run.
* `useWorkers` {boolean} Whether to run benchmarks in worker threads. **Default:** `false`.
* `plugins` {Array} Array of plugin instances to use.
* `minSamples` {number} Minimum number of samples per round for all benchmarks in the suite. Can be overridden per benchmark. **Default:** `10` samples.

If no `reporter` is provided, results are printed to the console.

Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export declare namespace BenchNode {
benchmarkMode?: "ops" | "time";
useWorkers?: boolean;
plugins?: Plugin[];
minSamples?: number; // Minimum number of samples per round for all benchmarks
}

interface BenchmarkOptions {
Expand Down
15 changes: 14 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class Suite {
#useWorkers;
#benchmarkMode;
#reporterOptions;
#minSamples;

constructor(options = {}) {
this.#benchmarks = [];
Expand Down Expand Up @@ -140,17 +141,29 @@ class Suite {
this.#reporterOptions = options.reporterOptions || {
printHeader: true,
};

// Suite-level minSamples option
if (options.minSamples !== undefined) {
validateNumber(options.minSamples, "options.minSamples", 1);
this.#minSamples = options.minSamples;
} else {
this.#minSamples = defaultBenchOptions.minSamples;
}
}

add(name, options, fn) {
validateString(name, "name");
if (typeof options === "function") {
fn = options;
options = defaultBenchOptions;
options = {
...defaultBenchOptions,
minSamples: this.#minSamples,
};
} else {
validateObject(options, "options");
options = {
...defaultBenchOptions,
minSamples: this.#minSamples,
...options,
};
// Enforce strict minimum (> 1e-6s). Using EPSILON to make boundary exclusive.
Expand Down
15 changes: 15 additions & 0 deletions test/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ describe("API Interface", () => {
}
});

it("suite-level minSamples should be a valid number", () => {
for (const r of ["ds", {}, () => {}]) {
assert.throws(
() => {
new Suite({ minSamples: r });
},
{
code: "ERR_INVALID_ARG_TYPE",
},
);
}
// doesNotThrow
new Suite({ minSamples: 20 });
});

describe("suite.add", () => {
const bench = new Suite({ reporter: noop });
it("name should be an string", () => {
Expand Down
3 changes: 3 additions & 0 deletions types/types.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ expectType<BenchNode.Suite>(
);
expectType<BenchNode.Suite>(new Suite({ reporter: false }));
expectType<BenchNode.Suite>(new Suite({ reporter: null }));
expectType<BenchNode.Suite>(new Suite({ minSamples: 20 }));

expectAssignable<BenchNode.SuiteOptions>({});
expectAssignable<BenchNode.SuiteOptions>({ reporter: chartReport });
Expand All @@ -35,8 +36,10 @@ expectAssignable<BenchNode.SuiteOptions>({ useWorkers: false });
expectAssignable<BenchNode.SuiteOptions>({
plugins: [new V8GetOptimizationStatus()],
});
expectAssignable<BenchNode.SuiteOptions>({ minSamples: 20 });
expectNotAssignable<BenchNode.SuiteOptions>({ unknownOption: "test" });
expectNotAssignable<BenchNode.SuiteOptions>({ reporter: "not-a-function" });
expectNotAssignable<BenchNode.SuiteOptions>({ minSamples: "not-a-number" });

expectAssignable<BenchNode.BenchmarkOptions>({});
expectAssignable<BenchNode.BenchmarkOptions>({ minTime: 0.1 });
Expand Down
Loading