Skip to content

Commit 4d5debc

Browse files
fixup! feat(tinybench-plugin): bump tinybench and add walltime support
1 parent 4efed1f commit 4d5debc

File tree

5 files changed

+85
-100
lines changed

5 files changed

+85
-100
lines changed

packages/core/src/index.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,26 @@ export const isBound = native_core.isBound;
1010

1111
export const mongoMeasurement = new MongoMeasurement();
1212

13-
export enum MeasurementMode {
14-
Instrumentation = "instrumentation",
15-
WallTime = "walltime",
16-
}
13+
type CodSpeedRunnerMode = "disabled" | "instrumented" | "walltime";
1714

18-
export function getMeasurementMode(): MeasurementMode {
15+
export function getCodspeedRunnerMode(): CodSpeedRunnerMode {
1916
const isCodSpeedEnabled = process.env.CODSPEED_ENV !== undefined;
20-
if (isCodSpeedEnabled) {
21-
// If CODSPEED_ENV is set, check CODSPEED_RUNNER_MODE
22-
if (process.env.CODSPEED_RUNNER_MODE === "walltime") {
23-
return MeasurementMode.WallTime;
24-
} else {
25-
return MeasurementMode.Instrumentation;
26-
}
17+
if (!isCodSpeedEnabled) {
18+
return "disabled";
19+
}
20+
21+
// If CODSPEED_ENV is set, check CODSPEED_RUNNER_MODE
22+
const codspeedRunnerMode = process.env.CODSPEED_RUNNER_MODE;
23+
if (codspeedRunnerMode === "instrumentation") {
24+
return "instrumented";
25+
} else if (codspeedRunnerMode === "walltime") {
26+
return "walltime";
2727
}
2828

29-
// Default to walltime mode when CODSPEED_ENV is not set
30-
return MeasurementMode.WallTime;
29+
console.warn(
30+
`Unknown codspeed runner mode: ${codspeedRunnerMode}, defaulting to disabled`
31+
);
32+
return "disabled";
3133
}
3234

3335
export const setupCore = () => {

packages/tinybench-plugin/src/index.ts

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
getCodspeedRunnerMode,
23
getGitDir,
34
Measurement,
45
mongoMeasurement,
@@ -11,53 +12,33 @@ import { get as getStackTrace } from "stack-trace";
1112
import { Bench } from "tinybench";
1213
import { fileURLToPath } from "url";
1314
import { runInstrumentedBench } from "./instrumented";
15+
import { getOrCreateUriMap } from "./uri";
1416
import { runWalltimeBench } from "./walltime";
1517

1618
tryIntrospect();
1719

18-
// Store URI mapping externally since fnOpts is private
19-
export const taskUriMap = new WeakMap<Bench, Map<string, string>>();
20-
2120
export function withCodSpeed(bench: Bench): Bench {
22-
// Check if CODSPEED_ENV is defined
23-
if (!process.env.CODSPEED_ENV) {
24-
// If CODSPEED_ENV is not defined, return bench unchanged (run as normal tinybench)
21+
const codspeedRunnerMode = getCodspeedRunnerMode();
22+
if (codspeedRunnerMode === "disabled") {
2523
return bench;
2624
}
2725

2826
const rootCallingFile = getCallingFile();
2927

30-
// Initialize URI mapping for this bench instance
31-
if (!taskUriMap.has(bench)) {
32-
taskUriMap.set(bench, new Map());
33-
}
34-
const uriMap = taskUriMap.get(bench)!;
35-
36-
// Setup URI generation for tasks
28+
// Compute and register URI for bench
29+
const uriMap = getOrCreateUriMap(bench);
3730
const rawAdd = bench.add;
3831
bench.add = (name, fn, opts?) => {
3932
const callingFile = getCallingFile();
4033
const uri = `${callingFile}::${name}`;
41-
// Store URI mapping
4234
uriMap.set(name, uri);
4335
return rawAdd.bind(bench)(name, fn, opts);
4436
};
4537

46-
// Apply the appropriate measurement strategy based on CODSPEED_RUNNER_MODE
47-
const runnerMode = process.env.CODSPEED_RUNNER_MODE;
48-
49-
if (runnerMode === "instrumentation") {
38+
if (codspeedRunnerMode === "instrumented") {
5039
runInstrumentedBench(bench, rootCallingFile);
51-
} else if (runnerMode === "walltime") {
40+
} else if (codspeedRunnerMode === "walltime") {
5241
runWalltimeBench(bench, rootCallingFile);
53-
} else {
54-
const rawRun = bench.run;
55-
bench.run = async () => {
56-
console.warn(
57-
`[CodSpeed] CODSPEED_ENV is defined but CODSPEED_RUNNER_MODE="${runnerMode}" is not recognized, falling back to tinybench`
58-
);
59-
return await rawRun.bind(bench)();
60-
};
6142
}
6243

6344
return bench;

packages/tinybench-plugin/src/instrumented.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,11 @@ import {
55
setupCore,
66
teardownCore,
77
} from "@codspeed/core";
8-
import { Bench } from "tinybench";
9-
import { taskUriMap } from "./index";
8+
import { Bench, Fn, FnOptions } from "tinybench";
9+
import { getTaskUri } from "./uri";
1010

1111
declare const __VERSION__: string;
1212

13-
function getTaskUri(
14-
bench: Bench,
15-
taskName: string,
16-
rootCallingFile: string
17-
): string {
18-
const uriMap = taskUriMap.get(bench);
19-
return uriMap?.get(taskName) || `${rootCallingFile}::${taskName}`;
20-
}
21-
2213
export function runInstrumentedBench(
2314
bench: Bench,
2415
rootCallingFile: string
@@ -32,16 +23,16 @@ export function runInstrumentedBench(
3223
for (const task of bench.tasks) {
3324
const uri = getTaskUri(bench, task.name, rootCallingFile);
3425

35-
// Access private fnOpts to get hooks
36-
const fnOpts = (task as any).fnOpts;
26+
// Access private fields
27+
const { fnOpts, fn } = task as unknown as { fnOpts?: FnOptions; fn: Fn };
3728

3829
// Call beforeAll hook if it exists
3930
await fnOpts?.beforeAll?.call(task, "run");
4031

4132
// run optimizations
4233
await optimizeFunction(async () => {
4334
await fnOpts?.beforeEach?.call(task, "run");
44-
await (task as any).fn(); // Access private fn property
35+
await fn();
4536
await fnOpts?.afterEach?.call(task, "run");
4637
});
4738

@@ -52,7 +43,7 @@ export function runInstrumentedBench(
5243
global.gc?.();
5344
await (async function __codspeed_root_frame__() {
5445
Measurement.startInstrumentation();
55-
await (task as any).fn(); // Access private fn property
46+
await fn();
5647
Measurement.stopInstrumentation(uri);
5748
})();
5849
await mongoMeasurement.stop(uri);

packages/tinybench-plugin/src/uri.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Bench } from "tinybench";
2+
3+
// Store URI mapping externally since fnOpts is private
4+
export const taskUriMap = new WeakMap<Bench, Map<string, string>>();
5+
6+
export function getTaskUri(
7+
bench: Bench,
8+
taskName: string,
9+
rootCallingFile: string
10+
): string {
11+
const uriMap = taskUriMap.get(bench);
12+
return uriMap?.get(taskName) || `${rootCallingFile}::${taskName}`;
13+
}
14+
15+
export function getOrCreateUriMap(bench: Bench) {
16+
if (!taskUriMap.has(bench)) {
17+
taskUriMap.set(bench, new Map());
18+
}
19+
return taskUriMap.get(bench)!;
20+
}

packages/tinybench-plugin/src/walltime.ts

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,10 @@ import {
66
type BenchmarkStats,
77
} from "@codspeed/core";
88
import { Bench, TaskResult } from "tinybench";
9-
import { taskUriMap } from "./index";
9+
import { getTaskUri } from "./uri";
1010

1111
declare const __VERSION__: string;
1212

13-
function getTaskUri(
14-
bench: Bench,
15-
taskName: string,
16-
rootCallingFile: string
17-
): string {
18-
const uriMap = taskUriMap.get(bench);
19-
return uriMap?.get(taskName) || `${rootCallingFile}::${taskName}`;
20-
}
21-
22-
function convertTinybenchResultToBenchmarkStats(
23-
result: TaskResult,
24-
warmupIterations: number
25-
): BenchmarkStats {
26-
// All tinybench times are in milliseconds, convert to nanoseconds
27-
const ms_to_ns = (ms: number) => ms * 1_000_000;
28-
29-
const { min, max, mean, sd, samples } = result.latency;
30-
31-
// Get individual sample times in nanoseconds and sort them
32-
const sortedTimesNs = samples.map(ms_to_ns).sort((a, b) => a - b);
33-
const meanNs = ms_to_ns(mean);
34-
const stdevNs = ms_to_ns(sd);
35-
36-
const { q1_ns, q3_ns, median_ns, iqr_outlier_rounds, stdev_outlier_rounds } =
37-
calculateQuantiles({ meanNs, stdevNs, sortedTimesNs });
38-
39-
return {
40-
min_ns: ms_to_ns(min),
41-
max_ns: ms_to_ns(max),
42-
mean_ns: meanNs,
43-
stdev_ns: stdevNs,
44-
q1_ns,
45-
median_ns,
46-
q3_ns,
47-
total_time: result.totalTime / 1_000, // convert from ms to seconds
48-
iter_per_round: sortedTimesNs.length,
49-
rounds: 1, // Tinybench only runs one round
50-
iqr_outlier_rounds,
51-
stdev_outlier_rounds,
52-
warmup_iters: warmupIterations,
53-
};
54-
}
55-
5613
export function runWalltimeBench(bench: Bench, rootCallingFile: string): void {
5714
bench.run = async () => {
5815
console.log(
@@ -125,3 +82,37 @@ export function runWalltimeBench(bench: Bench, rootCallingFile: string): void {
12582
return results;
12683
};
12784
}
85+
86+
function convertTinybenchResultToBenchmarkStats(
87+
result: TaskResult,
88+
warmupIterations: number
89+
): BenchmarkStats {
90+
// All tinybench times are in milliseconds, convert to nanoseconds
91+
const ms_to_ns = (ms: number) => ms * 1_000_000;
92+
93+
const { min, max, mean, sd, samples } = result.latency;
94+
95+
// Get individual sample times in nanoseconds and sort them
96+
const sortedTimesNs = samples.map(ms_to_ns).sort((a, b) => a - b);
97+
const meanNs = ms_to_ns(mean);
98+
const stdevNs = ms_to_ns(sd);
99+
100+
const { q1_ns, q3_ns, median_ns, iqr_outlier_rounds, stdev_outlier_rounds } =
101+
calculateQuantiles({ meanNs, stdevNs, sortedTimesNs });
102+
103+
return {
104+
min_ns: ms_to_ns(min),
105+
max_ns: ms_to_ns(max),
106+
mean_ns: meanNs,
107+
stdev_ns: stdevNs,
108+
q1_ns,
109+
median_ns,
110+
q3_ns,
111+
total_time: result.totalTime / 1_000, // convert from ms to seconds
112+
iter_per_round: sortedTimesNs.length,
113+
rounds: 1, // Tinybench only runs one round
114+
iqr_outlier_rounds,
115+
stdev_outlier_rounds,
116+
warmup_iters: warmupIterations,
117+
};
118+
}

0 commit comments

Comments
 (0)