Skip to content

Commit 792ee5f

Browse files
committed
fixup! chore(vitest): emit benchmark markers
1 parent f2b5162 commit 792ee5f

7 files changed

Lines changed: 47 additions & 36 deletions

File tree

packages/benchmark.js-plugin/src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
SetupInstrumentsResponse,
1010
teardownCore,
1111
tryIntrospect,
12+
wrapWithRootFrame,
13+
wrapWithRootFrameSync,
1214
} from "@codspeed/core";
1315
import Benchmark from "benchmark";
1416
import buildSuiteAdd from "./buildSuiteAdd";
@@ -195,7 +197,7 @@ async function runBenchmarks({
195197
await optimizeFunction(benchPayload);
196198
await mongoMeasurement.start(uri);
197199
global.gc?.();
198-
await (async function __codspeed_root_frame__() {
200+
await wrapWithRootFrame(async () => {
199201
InstrumentHooks.startBenchmark();
200202
await benchPayload();
201203
InstrumentHooks.stopBenchmark();
@@ -205,7 +207,7 @@ async function runBenchmarks({
205207
} else {
206208
optimizeFunctionSync(benchPayload);
207209
await mongoMeasurement.start(uri);
208-
(function __codspeed_root_frame__() {
210+
wrapWithRootFrameSync(() => {
209211
InstrumentHooks.startBenchmark();
210212
benchPayload();
211213
InstrumentHooks.stopBenchmark();

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export type {
7777
} from "./generated/openapi";
7878
export { getV8Flags, tryIntrospect } from "./introspection";
7979
export { optimizeFunction, optimizeFunctionSync } from "./optimization";
80+
export { wrapWithRootFrame, wrapWithRootFrameSync } from "./rootFrame";
8081
export * from "./utils";
8182
export * from "./walltime";
8283
export type { InstrumentMode };

packages/core/src/rootFrame.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Wrap a benchmark function so it executes under a frame named
3+
* `__codspeed_root_frame__`. CodSpeed uses this frame to locate the
4+
* benchmark root in collected call stacks; samples without it cannot be
5+
* attributed to a benchmark.
6+
*/
7+
export function wrapWithRootFrame<T>(
8+
fn: () => T | Promise<T>,
9+
): () => Promise<T> {
10+
return async function __codspeed_root_frame__() {
11+
return await fn();
12+
};
13+
}
14+
15+
export function wrapWithRootFrameSync<T>(fn: () => T): () => T {
16+
return function __codspeed_root_frame__() {
17+
return fn();
18+
};
19+
}

packages/tinybench-plugin/src/analysis.ts

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
InstrumentHooks,
44
mongoMeasurement,
55
optimizeFunction,
6+
wrapWithRootFrame,
7+
wrapWithRootFrameSync,
68
} from "@codspeed/core";
79
import { Bench, Fn, FnOptions, Task } from "tinybench";
810
import { BaseBenchRunner } from "./shared";
@@ -25,18 +27,6 @@ class AnalysisBenchRunner extends BaseBenchRunner {
2527
return InstrumentHooks.isInstrumented() ? "Measured" : "Checked";
2628
}
2729

28-
private wrapFunctionWithFrame(fn: Fn, isAsync: boolean): Fn {
29-
if (isAsync) {
30-
return async function __codspeed_root_frame__() {
31-
await fn();
32-
};
33-
} else {
34-
return function __codspeed_root_frame__() {
35-
fn();
36-
};
37-
}
38-
}
39-
4030
protected async runTaskAsync(task: Task, uri: string): Promise<void> {
4131
const { fnOpts, fn } = task as unknown as { fnOpts?: FnOptions; fn: Fn };
4232

@@ -50,10 +40,7 @@ class AnalysisBenchRunner extends BaseBenchRunner {
5040
await mongoMeasurement.start(uri);
5141

5242
global.gc?.();
53-
await this.wrapWithInstrumentHooksAsync(
54-
this.wrapFunctionWithFrame(fn, true),
55-
uri,
56-
);
43+
await this.wrapWithInstrumentHooksAsync(wrapWithRootFrame(fn), uri);
5744

5845
await mongoMeasurement.stop(uri);
5946
await fnOpts?.afterEach?.call(task, "run");
@@ -68,7 +55,7 @@ class AnalysisBenchRunner extends BaseBenchRunner {
6855
fnOpts?.beforeAll?.call(task, "run");
6956
fnOpts?.beforeEach?.call(task, "run");
7057

71-
this.wrapWithInstrumentHooks(this.wrapFunctionWithFrame(fn, false), uri);
58+
this.wrapWithInstrumentHooks(wrapWithRootFrameSync(fn), uri);
7259

7360
fnOpts?.afterEach?.call(task, "run");
7461
fnOpts?.afterAll?.call(task, "run");

packages/tinybench-plugin/src/walltime.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
mongoMeasurement,
44
msToNs,
55
msToS,
6+
wrapWithRootFrame,
7+
wrapWithRootFrameSync,
68
writeWalltimeResults,
79
type BenchmarkStats,
810
type Benchmark as CodspeedBenchmark,
@@ -64,21 +66,10 @@ class WalltimeBenchRunner extends BaseBenchRunner {
6466

6567
private wrapTaskFunction(task: Task, isAsync: boolean): void {
6668
const { fn } = task as unknown as { fn: Fn };
67-
if (isAsync) {
68-
// eslint-disable-next-line no-inner-declarations
69-
async function __codspeed_root_frame__() {
70-
await fn();
71-
}
72-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
73-
(task as any).fn = __codspeed_root_frame__;
74-
} else {
75-
// eslint-disable-next-line no-inner-declarations
76-
function __codspeed_root_frame__() {
77-
fn();
78-
}
79-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
80-
(task as any).fn = __codspeed_root_frame__;
81-
}
69+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
70+
(task as any).fn = isAsync
71+
? wrapWithRootFrame(fn)
72+
: wrapWithRootFrameSync(fn);
8273
}
8374

8475
private registerCodspeedBenchmarkFromTask(task: Task): void {

packages/vitest-plugin/src/analysis.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
optimizeFunction,
66
setupCore,
77
teardownCore,
8+
wrapWithRootFrame,
89
} from "@codspeed/core";
910
import { Benchmark, type RunnerTestSuite } from "vitest";
1011
import { NodeBenchmarkRunner } from "vitest/runners";
@@ -47,7 +48,7 @@ async function runAnalysisBench(
4748
await callSuiteHook(suite, benchmark, "beforeEach");
4849
await mongoMeasurement.start(uri);
4950
global.gc?.();
50-
await (async function __codspeed_root_frame__() {
51+
await wrapWithRootFrame(async () => {
5152
InstrumentHooks.startBenchmark();
5253
// @ts-expect-error we do not need to bind the function to an instance of tinybench's Bench
5354
await fn();

packages/vitest-plugin/src/walltime/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
MARKER_TYPE_BENCHMARK_END,
44
MARKER_TYPE_BENCHMARK_START,
55
setupCore,
6+
wrapWithRootFrame,
67
writeWalltimeResults,
78
} from "@codspeed/core";
89
import {
@@ -79,9 +80,18 @@ export class WalltimeRunner extends NodeBenchmarkRunner {
7980
tinybench.Task.prototype.run = async function () {
8081
const suiteUri = getSuiteUri();
8182

83+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
84+
const task = this as any;
85+
const originalFn = task.fn;
86+
task.fn = wrapWithRootFrame(() => originalFn.call(task));
87+
8288
InstrumentHooks.startBenchmark();
8389
const runStart = InstrumentHooks.currentTimestamp();
84-
await originalRun.call(this);
90+
try {
91+
await originalRun.call(this);
92+
} finally {
93+
task.fn = originalFn;
94+
}
8595
const runEnd = InstrumentHooks.currentTimestamp();
8696
InstrumentHooks.stopBenchmark();
8797

0 commit comments

Comments
 (0)