Skip to content

Commit d9c546a

Browse files
refactor(tinybench): share structure across walltime and instrumented
1 parent 7577855 commit d9c546a

6 files changed

Lines changed: 241 additions & 200 deletions

File tree

packages/tinybench-plugin/src/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
getGitDir,
44
InstrumentHooks,
55
mongoMeasurement,
6-
setupCore,
76
SetupInstrumentsRequestBody,
87
SetupInstrumentsResponse,
98
tryIntrospect,
@@ -23,7 +22,6 @@ export function withCodSpeed(bench: Bench): Bench {
2322
if (codspeedRunnerMode === "disabled") {
2423
return bench;
2524
}
26-
setupCore();
2725

2826
const rootCallingFile = getCallingFile();
2927

packages/tinybench-plugin/src/instrumented.ts

Lines changed: 52 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,34 @@ import {
22
InstrumentHooks,
33
mongoMeasurement,
44
optimizeFunction,
5-
teardownCore,
65
} from "@codspeed/core";
76
import { Bench, Fn, FnOptions, Task } from "tinybench";
8-
import { getTaskUri } from "./uri";
9-
10-
declare const __VERSION__: string;
7+
import { BaseBenchRunner } from "./shared";
8+
9+
class InstrumentedBenchRunner extends BaseBenchRunner {
10+
protected getModeName(): string {
11+
return "instrumented mode";
12+
}
13+
14+
private taskCompletionMessage() {
15+
return InstrumentHooks.isInstrumented() ? "Measured" : "Checked";
16+
}
17+
18+
private wrapFunctionWithFrame(fn: Fn, isAsync: boolean): Fn {
19+
if (isAsync) {
20+
return async function __codspeed_root_frame__() {
21+
global.gc?.();
22+
await fn();
23+
};
24+
} else {
25+
return function __codspeed_root_frame__() {
26+
global.gc?.();
27+
fn();
28+
};
29+
}
30+
}
1131

12-
export function runInstrumentedBench(
13-
bench: Bench,
14-
rootCallingFile: string
15-
): void {
16-
const runTaskAsync = async (task: Task, uri: string): Promise<void> => {
32+
protected async runTaskAsync(task: Task, uri: string): Promise<void> {
1733
const { fnOpts, fn } = task as unknown as { fnOpts?: FnOptions; fn: Fn };
1834

1935
await fnOpts?.beforeAll?.call(task, "run");
@@ -25,79 +41,48 @@ export function runInstrumentedBench(
2541
await fnOpts?.beforeEach?.call(task, "run");
2642
await mongoMeasurement.start(uri);
2743

28-
await (async function __codspeed_root_frame__() {
29-
global.gc?.();
30-
InstrumentHooks.startBenchmark();
31-
await fn();
32-
InstrumentHooks.stopBenchmark();
33-
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
34-
})();
44+
await this.wrapWithHooksAsync(
45+
this.wrapFunctionWithFrame(fn, true) as () => Promise<void>,
46+
uri
47+
);
3548

3649
await mongoMeasurement.stop(uri);
3750
await fnOpts?.afterEach?.call(task, "run");
3851
await fnOpts?.afterAll?.call(task, "run");
39-
};
4052

41-
// Sync task runner
42-
const runTaskSync = (task: Task, uri: string): void => {
53+
this.logTaskCompletion(uri, this.taskCompletionMessage());
54+
}
55+
56+
protected runTaskSync(task: Task, uri: string): void {
4357
const { fnOpts, fn } = task as unknown as { fnOpts?: FnOptions; fn: Fn };
4458

4559
fnOpts?.beforeAll?.call(task, "run");
4660
fnOpts?.beforeEach?.call(task, "run");
4761

48-
(function __codspeed_root_frame__() {
49-
global.gc?.();
50-
InstrumentHooks.startBenchmark();
51-
fn();
52-
InstrumentHooks.stopBenchmark();
53-
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
54-
})();
62+
this.wrapWithHooks(
63+
this.wrapFunctionWithFrame(fn, false) as () => void,
64+
uri
65+
);
5566

5667
fnOpts?.afterEach?.call(task, "run");
5768
fnOpts?.afterAll?.call(task, "run");
58-
};
59-
60-
bench.run = async () => {
61-
logStart();
62-
63-
for (const task of bench.tasks) {
64-
const uri = getTaskUri(bench, task.name, rootCallingFile);
65-
await runTaskAsync(task, uri);
66-
logTaskCompletion(uri);
67-
}
68-
69-
return logEnd();
70-
};
71-
72-
bench.runSync = () => {
73-
logStart();
7469

75-
for (const task of bench.tasks) {
76-
const uri = getTaskUri(bench, task.name, rootCallingFile);
77-
runTaskSync(task, uri);
78-
logTaskCompletion(uri);
79-
}
80-
81-
return logEnd();
82-
};
70+
this.logTaskCompletion(uri, this.taskCompletionMessage());
71+
}
8372

84-
const logStart = () => {
85-
console.log(
86-
`[CodSpeed] running with @codspeed/tinybench v${__VERSION__} (instrumented mode)`
87-
);
88-
};
73+
protected finalizeAsyncRun(): Task[] {
74+
return this.finalizeBenchRun();
75+
}
8976

90-
const logTaskCompletion = (uri: string) => {
91-
console.log(
92-
` ✔ ${
93-
InstrumentHooks.isInstrumented() ? "Measured" : "Checked"
94-
} ${uri}`
95-
);
96-
};
77+
protected finalizeSyncRun(): Task[] {
78+
return this.finalizeBenchRun();
79+
}
80+
}
9781

98-
const logEnd = () => {
99-
teardownCore();
100-
console.log(`[CodSpeed] Done running ${bench.tasks.length} benches.`);
101-
return bench.tasks;
102-
};
82+
export function runInstrumentedBench(
83+
bench: Bench,
84+
rootCallingFile: string
85+
): void {
86+
const runner = new InstrumentedBenchRunner(bench, rootCallingFile);
87+
runner.setupBenchMethods();
10388
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { InstrumentHooks, setupCore, teardownCore } from "@codspeed/core";
2+
import { Bench, Task } from "tinybench";
3+
import { getTaskUri } from "./uri";
4+
5+
declare const __VERSION__: string;
6+
7+
export abstract class BaseBenchRunner {
8+
protected bench: Bench;
9+
protected rootCallingFile: string;
10+
11+
constructor(bench: Bench, rootCallingFile: string) {
12+
this.bench = bench;
13+
this.rootCallingFile = rootCallingFile;
14+
}
15+
16+
private setupBenchRun(): void {
17+
setupCore();
18+
this.logStart();
19+
}
20+
21+
private logStart(): void {
22+
console.log(
23+
`[CodSpeed] running with @codspeed/tinybench v${__VERSION__} (${this.getModeName()})`
24+
);
25+
}
26+
27+
protected getTaskUri(task: Task): string {
28+
return getTaskUri(this.bench, task.name, this.rootCallingFile);
29+
}
30+
31+
protected logTaskCompletion(uri: string, status: string): void {
32+
console.log(`[CodSpeed] ${status} ${uri}`);
33+
}
34+
35+
protected finalizeBenchRun(): Task[] {
36+
teardownCore();
37+
console.log(`[CodSpeed] Done running ${this.bench.tasks.length} benches.`);
38+
return this.bench.tasks;
39+
}
40+
41+
protected wrapWithHooks<T>(fn: () => T, uri: string): T {
42+
InstrumentHooks.startBenchmark();
43+
const result = fn();
44+
InstrumentHooks.stopBenchmark();
45+
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
46+
return result;
47+
}
48+
49+
protected async wrapWithHooksAsync<T>(
50+
fn: () => Promise<T>,
51+
uri: string
52+
): Promise<T> {
53+
InstrumentHooks.startBenchmark();
54+
const result = await fn();
55+
InstrumentHooks.stopBenchmark();
56+
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
57+
return result;
58+
}
59+
60+
protected abstract getModeName(): string;
61+
protected abstract runTaskAsync(task: Task, uri: string): Promise<void>;
62+
protected abstract runTaskSync(task: Task, uri: string): void;
63+
protected abstract finalizeAsyncRun(): Task[];
64+
protected abstract finalizeSyncRun(): Task[];
65+
66+
public setupBenchMethods(): void {
67+
this.bench.run = async () => {
68+
this.setupBenchRun();
69+
70+
for (const task of this.bench.tasks) {
71+
const uri = this.getTaskUri(task);
72+
await this.runTaskAsync(task, uri);
73+
}
74+
75+
return this.finalizeAsyncRun();
76+
};
77+
78+
this.bench.runSync = () => {
79+
this.setupBenchRun();
80+
81+
for (const task of this.bench.tasks) {
82+
const uri = this.getTaskUri(task);
83+
this.runTaskSync(task, uri);
84+
}
85+
86+
return this.finalizeSyncRun();
87+
};
88+
}
89+
}

0 commit comments

Comments
 (0)