Skip to content

Commit 8b062ba

Browse files
GuillaumeLagrangeart049
authored andcommitted
feat(tinybench-plugin): add support for perf profiling
Support is still far from perfect for async/heavy code.
1 parent cef4fa4 commit 8b062ba

File tree

4 files changed

+101
-12
lines changed

4 files changed

+101
-12
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ yarn-error.log*
77
lerna-debug.log*
88
.pnpm-debug.log*
99

10+
# JIT dumps
11+
jit-*.dump
12+
1013
# Diagnostic reports (https://nodejs.org/api/report.html)
1114
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
1215

packages/tinybench-plugin/benches/sample.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ bench
3535
});
3636

3737
(async () => {
38-
await bench.run();
38+
bench.runSync();
3939
console.table(bench.table());
4040

4141
const timingBench = withCodSpeed(
@@ -44,6 +44,6 @@ bench
4444

4545
registerTimingBenchmarks(timingBench);
4646

47-
await timingBench.run();
47+
timingBench.runSync();
4848
console.table(timingBench.table());
4949
})();

packages/tinybench-plugin/benches/timing.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ const busySleep = (ms: number): void => {
88
};
99

1010
export function registerTimingBenchmarks(bench: Bench) {
11-
bench.add("wait 1ms", async () => {
11+
bench.add("wait 1ms", () => {
1212
busySleep(1);
1313
});
1414

15-
bench.add("wait 500ms", async () => {
15+
bench.add("wait 500ms", () => {
1616
busySleep(500);
1717
});
1818

19-
bench.add("wait 1sec", async () => {
19+
bench.add("wait 1sec", () => {
2020
busySleep(1000);
2121
});
2222
}

packages/tinybench-plugin/src/walltime.ts

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import {
22
calculateQuantiles,
3+
InstrumentHooks,
34
mongoMeasurement,
45
msToNs,
56
msToS,
67
writeWalltimeResults,
78
type Benchmark,
89
type BenchmarkStats,
910
} from "@codspeed/core";
10-
import { Bench, TaskResult } from "tinybench";
11+
import { Bench, Fn, TaskResult } from "tinybench";
1112
import { getTaskUri } from "./uri";
1213

1314
declare const __VERSION__: string;
@@ -28,21 +29,27 @@ export function runWalltimeBench(bench: Bench, rootCallingFile: string): void {
2829

2930
const benchmarks: Benchmark[] = [];
3031

31-
// Run the bench naturally to collect TaskResult data
32-
const results = [];
33-
3432
// Collect and report walltime data
3533
for (const task of bench.tasks) {
3634
const uri = getTaskUri(bench, task.name, rootCallingFile);
3735

36+
// Override the function under test to add a static frame
37+
const { fn } = task as unknown as { fn: Fn };
38+
async function __codspeed_root_frame__() {
39+
await fn();
40+
}
41+
(task as any).fn = __codspeed_root_frame__;
42+
3843
// run the warmup of the task right before its actual run
3944
if (bench.opts.warmup) {
4045
await task.warmup();
4146
}
47+
4248
await mongoMeasurement.start(uri);
43-
const taskResult = await task.run();
49+
InstrumentHooks.startBenchmark();
50+
await task.run();
51+
InstrumentHooks.stopBenchmark();
4452
await mongoMeasurement.stop(uri);
45-
results.push(taskResult);
4653

4754
if (task.result) {
4855
// Convert tinybench result to BenchmarkStats format
@@ -67,8 +74,87 @@ export function runWalltimeBench(bench: Bench, rootCallingFile: string): void {
6774
};
6875

6976
benchmarks.push(benchmark);
77+
console.log(` ✔ Collected walltime data for ${uri}`);
78+
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
79+
} else {
80+
console.warn(` ⚠ No result data available for ${uri}`);
81+
}
82+
}
83+
84+
// Write results to JSON file using core function
85+
if (benchmarks.length > 0) {
86+
writeWalltimeResults(benchmarks);
87+
}
88+
89+
console.log(
90+
`[CodSpeed] Done collecting walltime data for ${bench.tasks.length} benches.`
91+
);
92+
// Restore our custom run method
93+
bench.run = originalRun;
94+
95+
return bench.tasks;
96+
};
97+
98+
bench.runSync = () => {
99+
console.log(
100+
`[CodSpeed] running with @codspeed/tinybench v${__VERSION__} (walltime mode)`
101+
);
102+
103+
// Store the original run method before we override it
104+
const originalRun = bench.run;
105+
106+
// Temporarily restore the original run to get actual benchmark results
107+
const benchProto = Object.getPrototypeOf(bench);
108+
const prototypeRun = benchProto.run;
109+
bench.run = prototypeRun;
110+
111+
const benchmarks: Benchmark[] = [];
112+
113+
// Collect and report walltime data
114+
for (const task of bench.tasks) {
115+
const uri = getTaskUri(bench, task.name, rootCallingFile);
116+
117+
// run the warmup of the task right before its actual run
118+
if (bench.opts.warmup) {
119+
task.warmup();
120+
}
70121

122+
// Override the function under test to add a static frame
123+
const { fn } = task as unknown as { fn: Fn };
124+
function __codspeed_root_frame__() {
125+
fn();
126+
}
127+
(task as any).fn = __codspeed_root_frame__;
128+
129+
InstrumentHooks.startBenchmark();
130+
task.runSync();
131+
InstrumentHooks.stopBenchmark();
132+
133+
if (task.result) {
134+
// Convert tinybench result to BenchmarkStats format
135+
const stats = convertTinybenchResultToBenchmarkStats(
136+
task.result,
137+
bench.opts.warmup ? bench.opts.warmupIterations ?? 0 : 0
138+
);
139+
140+
const benchmark: Benchmark = {
141+
name: task.name,
142+
uri,
143+
config: {
144+
max_rounds: bench.opts.iterations ?? null,
145+
max_time_ns: bench.opts.time ? msToNs(bench.opts.time) : null,
146+
min_round_time_ns: null, // tinybench does not have an option for this
147+
warmup_time_ns:
148+
bench.opts.warmup && bench.opts.warmupTime
149+
? msToNs(bench.opts.warmupTime)
150+
: null,
151+
},
152+
stats,
153+
};
154+
155+
benchmarks.push(benchmark);
71156
console.log(` ✔ Collected walltime data for ${uri}`);
157+
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
72158
} else {
73159
console.warn(` ⚠ No result data available for ${uri}`);
74160
}
@@ -85,7 +171,7 @@ export function runWalltimeBench(bench: Bench, rootCallingFile: string): void {
85171
// Restore our custom run method
86172
bench.run = originalRun;
87173

88-
return results;
174+
return bench.tasks;
89175
};
90176
}
91177

0 commit comments

Comments
 (0)