@@ -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" ;
1313import { BaseBenchRunner } from "./shared" ;
1414
1515export 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
2324class 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