Skip to content

Commit 4173870

Browse files
GuillaumeLagrangeart049
authored andcommitted
refactor(tinybench): share structure across walltime and instrumented
1 parent 44cf175 commit 4173870

File tree

7 files changed

+249
-207
lines changed

7 files changed

+249
-207
lines changed

packages/tinybench-plugin/src/index.ts

Lines changed: 4 additions & 6 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,
@@ -12,9 +11,9 @@ import path from "path";
1211
import { get as getStackTrace } from "stack-trace";
1312
import { Bench } from "tinybench";
1413
import { fileURLToPath } from "url";
15-
import { runInstrumentedBench } from "./instrumented";
14+
import { setupCodspeedInstrumentedBench } from "./instrumented";
1615
import { getOrCreateUriMap } from "./uri";
17-
import { runWalltimeBench } from "./walltime";
16+
import { setupCodspeedWalltimeBench } from "./walltime";
1817

1918
tryIntrospect();
2019

@@ -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

@@ -42,9 +40,9 @@ export function withCodSpeed(bench: Bench): Bench {
4240
};
4341

4442
if (codspeedRunnerMode === "instrumented") {
45-
runInstrumentedBench(bench, rootCallingFile);
43+
setupCodspeedInstrumentedBench(bench, rootCallingFile);
4644
} else if (codspeedRunnerMode === "walltime") {
47-
runWalltimeBench(bench, rootCallingFile);
45+
setupCodspeedWalltimeBench(bench, rootCallingFile);
4846
}
4947

5048
return bench;

packages/tinybench-plugin/src/index.unit.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
33
import { withCodSpeed } from ".";
44

55
const mockInstrumented = vi.hoisted(() => ({
6-
runInstrumentedBench: vi.fn(),
6+
setupCodspeedInstrumentedBench: vi.fn(),
77
}));
88

99
vi.mock("./instrumented", () => ({
1010
...mockInstrumented,
1111
}));
1212

1313
const mockWalltime = vi.hoisted(() => ({
14-
runWalltimeBench: vi.fn(),
14+
setupCodspeedWalltimeBench: vi.fn(),
1515
}));
1616

1717
vi.mock("./walltime", () => ({
@@ -44,8 +44,8 @@ describe("withCodSpeed behavior without different codspeed modes", () => {
4444

4545
withCodSpeed(new Bench());
4646

47-
expect(mockInstrumented.runInstrumentedBench).toHaveBeenCalled();
48-
expect(mockWalltime.runWalltimeBench).not.toHaveBeenCalled();
47+
expect(mockInstrumented.setupCodspeedInstrumentedBench).toHaveBeenCalled();
48+
expect(mockWalltime.setupCodspeedWalltimeBench).not.toHaveBeenCalled();
4949
});
5050

5151
it("should run in walltime mode when CODSPEED_RUNNER_MODE=walltime", async () => {
@@ -54,7 +54,9 @@ describe("withCodSpeed behavior without different codspeed modes", () => {
5454

5555
withCodSpeed(new Bench());
5656

57-
expect(mockInstrumented.runInstrumentedBench).not.toHaveBeenCalled();
58-
expect(mockWalltime.runWalltimeBench).toHaveBeenCalled();
57+
expect(
58+
mockInstrumented.setupCodspeedInstrumentedBench
59+
).not.toHaveBeenCalled();
60+
expect(mockWalltime.setupCodspeedWalltimeBench).toHaveBeenCalled();
5961
});
6062
});

packages/tinybench-plugin/src/instrumented.ts

Lines changed: 46 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,40 @@ 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";
7+
import { BaseBenchRunner } from "./shared";
98

10-
declare const __VERSION__: string;
11-
12-
export function runInstrumentedBench(
9+
export function setupCodspeedInstrumentedBench(
1310
bench: Bench,
1411
rootCallingFile: string
1512
): void {
16-
const runTaskAsync = async (task: Task, uri: string): Promise<void> => {
13+
const runner = new InstrumentedBenchRunner(bench, rootCallingFile);
14+
runner.setupBenchMethods();
15+
}
16+
17+
class InstrumentedBenchRunner extends BaseBenchRunner {
18+
protected getModeName(): string {
19+
return "instrumented mode";
20+
}
21+
22+
private taskCompletionMessage() {
23+
return InstrumentHooks.isInstrumented() ? "Measured" : "Checked";
24+
}
25+
26+
private wrapFunctionWithFrame(fn: Fn, isAsync: boolean): Fn {
27+
if (isAsync) {
28+
return async function __codspeed_root_frame__() {
29+
await fn();
30+
};
31+
} else {
32+
return function __codspeed_root_frame__() {
33+
fn();
34+
};
35+
}
36+
}
37+
38+
protected async runTaskAsync(task: Task, uri: string): Promise<void> {
1739
const { fnOpts, fn } = task as unknown as { fnOpts?: FnOptions; fn: Fn };
1840

1941
await fnOpts?.beforeAll?.call(task, "run");
@@ -25,79 +47,38 @@ export function runInstrumentedBench(
2547
await fnOpts?.beforeEach?.call(task, "run");
2648
await mongoMeasurement.start(uri);
2749

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-
})();
50+
global.gc?.();
51+
await this.wrapWithInstrumentHooksAsync(
52+
this.wrapFunctionWithFrame(fn, true),
53+
uri
54+
);
3555

3656
await mongoMeasurement.stop(uri);
3757
await fnOpts?.afterEach?.call(task, "run");
3858
await fnOpts?.afterAll?.call(task, "run");
39-
};
4059

41-
// Sync task runner
42-
const runTaskSync = (task: Task, uri: string): void => {
60+
this.logTaskCompletion(uri, this.taskCompletionMessage());
61+
}
62+
63+
protected runTaskSync(task: Task, uri: string): void {
4364
const { fnOpts, fn } = task as unknown as { fnOpts?: FnOptions; fn: Fn };
4465

4566
fnOpts?.beforeAll?.call(task, "run");
4667
fnOpts?.beforeEach?.call(task, "run");
4768

48-
(function __codspeed_root_frame__() {
49-
global.gc?.();
50-
InstrumentHooks.startBenchmark();
51-
fn();
52-
InstrumentHooks.stopBenchmark();
53-
InstrumentHooks.setExecutedBenchmark(process.pid, uri);
54-
})();
69+
this.wrapWithInstrumentHooks(this.wrapFunctionWithFrame(fn, false), uri);
5570

5671
fnOpts?.afterEach?.call(task, "run");
5772
fnOpts?.afterAll?.call(task, "run");
58-
};
59-
60-
bench.run = async () => {
61-
logStart();
6273

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();
74-
75-
for (const task of bench.tasks) {
76-
const uri = getTaskUri(bench, task.name, rootCallingFile);
77-
runTaskSync(task, uri);
78-
logTaskCompletion(uri);
79-
}
74+
this.logTaskCompletion(uri, this.taskCompletionMessage());
75+
}
8076

81-
return logEnd();
82-
};
83-
84-
const logStart = () => {
85-
console.log(
86-
`[CodSpeed] running with @codspeed/tinybench v${__VERSION__} (instrumented mode)`
87-
);
88-
};
89-
90-
const logTaskCompletion = (uri: string) => {
91-
console.log(
92-
` ✔ ${
93-
InstrumentHooks.isInstrumented() ? "Measured" : "Checked"
94-
} ${uri}`
95-
);
96-
};
77+
protected finalizeAsyncRun(): Task[] {
78+
return this.finalizeBenchRun();
79+
}
9780

98-
const logEnd = () => {
99-
teardownCore();
100-
console.log(`[CodSpeed] Done running ${bench.tasks.length} benches.`);
101-
return bench.tasks;
102-
};
81+
protected finalizeSyncRun(): Task[] {
82+
return this.finalizeBenchRun();
83+
}
10384
}
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, Fn, 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 wrapWithInstrumentHooks<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 wrapWithInstrumentHooksAsync(
50+
fn: Fn,
51+
uri: string
52+
): Promise<unknown> {
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)