Skip to content

Commit e07237e

Browse files
fix(tinybench-plugin): fix profiler capturing tinybench's internal functions
1 parent 1fb37ef commit e07237e

3 files changed

Lines changed: 64 additions & 17 deletions

File tree

CLAUDE.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,10 @@ Based on the codebase analysis, to add stats access features:
8888
## Repository Management Memories
8989

9090
- Use pnpm instead of npm
91-
- To run tests in a package use moon <package-name>:test
91+
- The monorepo uses [Turborepo](https://turborepo.com). Run a task for a single
92+
package with a filter, using the package's full name (e.g. `@codspeed/tinybench-plugin`):
93+
- Test: `pnpm turbo run test --filter=<package-name>`
94+
- Build: `pnpm turbo run build --filter=<package-name>`
95+
- Typecheck: `pnpm turbo run typecheck --filter=<package-name>`
96+
- Lint: `pnpm turbo run lint --filter=<package-name>`
97+
- Run a task across all packages by omitting `--filter` (e.g. `pnpm turbo run build`).

packages/tinybench-plugin/src/shared.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,34 +46,38 @@ export abstract class BaseBenchRunner {
4646
}
4747

4848
protected wrapWithInstrumentHooks<T>(fn: () => T, uri: string): T {
49-
InstrumentHooks.startBenchmark();
50-
const runStart = InstrumentHooks.currentTimestamp();
49+
const runStart = this.openInstrumentWindow();
5150
try {
5251
return fn();
5352
} finally {
54-
const runEnd = InstrumentHooks.currentTimestamp();
55-
InstrumentHooks.stopBenchmark();
56-
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
57-
this.sendBenchmarkMarkers(runStart, runEnd);
53+
this.closeInstrumentWindow(uri, runStart);
5854
}
5955
}
6056

6157
protected async wrapWithInstrumentHooksAsync(
6258
fn: Fn,
6359
uri: string,
6460
): Promise<unknown> {
65-
InstrumentHooks.startBenchmark();
66-
const runStart = InstrumentHooks.currentTimestamp();
61+
const runStart = this.openInstrumentWindow();
6762
try {
6863
return await fn();
6964
} finally {
70-
const runEnd = InstrumentHooks.currentTimestamp();
71-
InstrumentHooks.stopBenchmark();
72-
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
73-
this.sendBenchmarkMarkers(runStart, runEnd);
65+
this.closeInstrumentWindow(uri, runStart);
7466
}
7567
}
7668

69+
protected openInstrumentWindow(): bigint {
70+
InstrumentHooks.startBenchmark();
71+
return InstrumentHooks.currentTimestamp();
72+
}
73+
74+
protected closeInstrumentWindow(uri: string, runStart: bigint): void {
75+
const runEnd = InstrumentHooks.currentTimestamp();
76+
InstrumentHooks.stopBenchmark();
77+
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
78+
this.sendBenchmarkMarkers(runStart, runEnd);
79+
}
80+
7781
protected abstract getModeName(): string;
7882
protected abstract runTaskAsync(task: Task, uri: string): Promise<void>;
7983
protected abstract runTaskSync(task: Task, uri: string): void;

packages/tinybench-plugin/src/walltime.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,60 @@ import {
99
type BenchmarkStats,
1010
type Benchmark as CodspeedBenchmark,
1111
} from "@codspeed/core";
12-
import { Bench, Fn, Task, TaskResult } from "tinybench";
12+
import { Bench, Fn, Hook, Task, TaskResult } from "tinybench";
1313
import { BaseBenchRunner } from "./shared";
1414

1515
export function setupCodspeedWalltimeBench(
1616
bench: Bench,
1717
rootCallingFile: string,
1818
): void {
1919
const runner = new WalltimeBenchRunner(bench, rootCallingFile);
20+
runner.installInstrumentHooks();
2021
runner.setupBenchMethods();
2122
}
2223

2324
class WalltimeBenchRunner extends BaseBenchRunner {
2425
private codspeedBenchmarks: CodspeedBenchmark[] = [];
2526

27+
// Carries the window start timestamp from the setup hook to the teardown
28+
// hook. Tasks run strictly sequentially, so a single field is enough.
29+
private runStart: bigint | null = null;
30+
2631
protected getModeName(): string {
2732
return "walltime mode";
2833
}
2934

35+
/**
36+
* Drive the instrumentation window from the task's setup/teardown hooks so it
37+
* brackets only tinybench's measured loop, excluding warmup and the
38+
* statistics computation (`processRunResult`) that surround it. The user's
39+
* own hooks are preserved and still run in their original order relative to
40+
* the work under test.
41+
*/
42+
public installInstrumentHooks(): void {
43+
// `bench.opts` is typed `Readonly`, but tinybench mutates it at runtime and
44+
// always resolves `setup`/`teardown` to (at least) a no-op default.
45+
const opts = this.bench.opts as { setup: Hook; teardown: Hook };
46+
const userSetup = opts.setup;
47+
const userTeardown = opts.teardown;
48+
49+
opts.setup = (task, mode) => {
50+
const setupResult = userSetup(task, mode);
51+
if (mode === "run") {
52+
this.runStart = this.openInstrumentWindow();
53+
}
54+
return setupResult;
55+
};
56+
57+
opts.teardown = (task, mode) => {
58+
if (mode === "run" && task) {
59+
this.closeInstrumentWindow(this.getTaskUri(task), this.runStart!);
60+
this.runStart = null;
61+
}
62+
return userTeardown(task, mode);
63+
};
64+
}
65+
3066
protected async runTaskAsync(task: Task, uri: string): Promise<void> {
3167
// Override the function under test to add a static frame
3268
this.wrapTaskFunction(task, true);
@@ -37,21 +73,22 @@ class WalltimeBenchRunner extends BaseBenchRunner {
3773
}
3874

3975
await mongoMeasurement.start(uri);
40-
await this.wrapWithInstrumentHooksAsync(() => task.run(), uri);
76+
await task.run();
4177
await mongoMeasurement.stop(uri);
4278

4379
this.registerCodspeedBenchmarkFromTask(task);
4480
}
4581

46-
protected runTaskSync(task: Task, uri: string): void {
82+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
83+
protected runTaskSync(task: Task, _uri: string): void {
4784
// Override the function under test to add a static frame
4885
this.wrapTaskFunction(task, false);
4986

5087
if (this.bench.opts.warmup) {
5188
task.warmup();
5289
}
5390

54-
this.wrapWithInstrumentHooks(() => task.runSync(), uri);
91+
task.runSync();
5592

5693
this.registerCodspeedBenchmarkFromTask(task);
5794
}

0 commit comments

Comments
 (0)