Skip to content

Commit c5a0729

Browse files
authored
fix: introduce and use BenchLike interface to decouple task from Bench. (#422)
1 parent 49aefb0 commit c5a0729

File tree

8 files changed

+133
-88
lines changed

8 files changed

+133
-88
lines changed

src/bench.ts

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import type {
22
AddEventListenerOptionsArgument,
33
BenchEvents,
4+
BenchLike,
45
BenchOptions,
56
EventListener,
67
EventListenerObject,
78
Fn,
89
FnOptions,
910
RemoveEventListenerOptionsArgument,
10-
ResolvedBenchOptions,
1111
TaskResult,
1212
} from './types'
1313

1414
import {
15-
defaultMinimumIterations,
16-
defaultMinimumTime,
17-
defaultMinimumWarmupIterations,
15+
defaultMinimumIterations as defaultIterations,
1816
defaultMinimumWarmupTime,
17+
defaultMinimumTime as defaultTime,
18+
defaultMinimumWarmupIterations as defaultWarmupIterations,
1919
emptyFunction,
2020
} from './constants'
2121
import { BenchEvent } from './event'
@@ -32,7 +32,7 @@ import {
3232
/**
3333
* The Bench class keeps track of the benchmark tasks and controls them.
3434
*/
35-
export class Bench extends EventTarget {
35+
export class Bench extends EventTarget implements BenchLike {
3636
declare addEventListener: <K extends BenchEvents>(
3737
type: K,
3838
listener: EventListener<K> | EventListenerObject<K> | null,
@@ -46,17 +46,16 @@ export class Bench extends EventTarget {
4646
* - When `mode` is set to 'task', each task's iterations (calls of a task function) run concurrently.
4747
* - When `mode` is set to 'bench', different tasks within the bench run concurrently.
4848
*/
49-
concurrency: 'bench' | 'task' | null = null
49+
readonly concurrency: 'bench' | 'task' | null = null
50+
51+
readonly iterations: number
5052

5153
/**
5254
* The benchmark name.
5355
*/
5456
readonly name: string | undefined
5557

56-
/**
57-
* The options.
58-
*/
59-
readonly opts: Readonly<ResolvedBenchOptions>
58+
readonly now: () => number
6059

6160
declare removeEventListener: <K extends BenchEvents>(
6261
type: K,
@@ -74,11 +73,27 @@ export class Bench extends EventTarget {
7473
*/
7574
readonly runtimeVersion: string
7675

76+
readonly setup: (task: Task, mode: 'run' | 'warmup') => Promise<void> | void
77+
78+
readonly signal: AbortSignal | undefined
79+
80+
readonly teardown: (task: Task, mode: 'run' | 'warmup') => Promise<void> | void
81+
7782
/**
7883
* The maximum number of concurrent tasks to run
7984
* @default Infinity
8085
*/
81-
threshold = Infinity
86+
readonly threshold = Infinity
87+
88+
readonly throws: boolean
89+
90+
readonly time: number
91+
92+
readonly warmup: boolean
93+
94+
readonly warmupIterations: number
95+
96+
readonly warmupTime: number
8297

8398
/**
8499
* tasks results as an array
@@ -110,23 +125,21 @@ export class Bench extends EventTarget {
110125
this.concurrency = restOptions.concurrency ?? null
111126
this.threshold = restOptions.threshold ?? Infinity
112127

113-
this.opts = {
114-
...{
115-
iterations: defaultMinimumIterations,
116-
now: performanceNow,
117-
setup: emptyFunction,
118-
teardown: emptyFunction,
119-
throws: false,
120-
time: defaultMinimumTime,
121-
warmup: true,
122-
warmupIterations: defaultMinimumWarmupIterations,
123-
warmupTime: defaultMinimumWarmupTime,
124-
},
125-
...restOptions,
126-
}
128+
this.time = restOptions.time ?? defaultTime
129+
this.iterations = restOptions.iterations ?? defaultIterations
130+
this.now = restOptions.now ?? performanceNow
131+
this.warmup = restOptions.warmup ?? true
132+
this.warmupIterations =
133+
restOptions.warmupIterations ?? defaultWarmupIterations
134+
this.warmupTime =
135+
restOptions.warmupTime ?? defaultMinimumWarmupTime
136+
this.setup = restOptions.setup ?? emptyFunction
137+
this.teardown = restOptions.teardown ?? emptyFunction
138+
this.throws = restOptions.throws ?? false
139+
this.signal = restOptions.signal
127140

128-
if (this.opts.signal) {
129-
this.opts.signal.addEventListener(
141+
if (this.signal) {
142+
this.signal.addEventListener(
130143
'abort',
131144
() => {
132145
this.dispatchEvent(new BenchEvent('abort'))
@@ -193,7 +206,7 @@ export class Bench extends EventTarget {
193206
* @returns the tasks array
194207
*/
195208
async run (): Promise<Task[]> {
196-
if (this.opts.warmup) {
209+
if (this.warmup) {
197210
await this.#warmupTasks()
198211
}
199212

@@ -226,7 +239,7 @@ export class Bench extends EventTarget {
226239
this.concurrency === null,
227240
'Cannot use `concurrency` option when using `runSync`'
228241
)
229-
if (this.opts.warmup) {
242+
if (this.warmup) {
230243
this.#warmupTasksSync()
231244
}
232245
const values: Task[] = []

src/task.ts

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { Bench } from './bench'
21
import type {
32
AddEventListenerOptionsArgument,
3+
BenchLike,
44
EventListener,
55
EventListenerObject,
66
Fn,
@@ -79,7 +79,7 @@ export class Task extends EventTarget {
7979
/**
8080
* The Bench instance reference
8181
*/
82-
readonly #bench: Bench
82+
readonly #bench: BenchLike
8383

8484
/**
8585
* The task function
@@ -111,7 +111,7 @@ export class Task extends EventTarget {
111111
*/
112112
readonly #signal: AbortSignal | undefined
113113

114-
constructor (bench: Bench, name: string, fn: Fn, fnOpts: FnOptions = {}) {
114+
constructor (bench: BenchLike, name: string, fn: Fn, fnOpts: FnOptions = {}) {
115115
super()
116116
this.#bench = bench
117117
this.#name = name
@@ -143,11 +143,11 @@ export class Task extends EventTarget {
143143
}
144144
}
145145

146-
if (this.#bench.opts.signal) {
147-
if (this.#bench.opts.signal.aborted) {
146+
if (this.#bench.signal) {
147+
if (this.#bench.signal.aborted) {
148148
this.#onAbort()
149149
} else {
150-
this.#bench.opts.signal.addEventListener(
150+
this.#bench.signal.addEventListener(
151151
'abort',
152152
this.#onAbort.bind(this),
153153
{ once: true }
@@ -179,13 +179,13 @@ export class Task extends EventTarget {
179179
}
180180
this.#result = { state: 'started' }
181181
this.dispatchEvent(new BenchEvent('start', this))
182-
await this.#bench.opts.setup(this, 'run')
182+
await this.#bench.setup(this, 'run')
183183
const { error, samples: latencySamples } = await this.#benchmark(
184184
'run',
185-
this.#bench.opts.time,
186-
this.#bench.opts.iterations
185+
this.#bench.time,
186+
this.#bench.iterations
187187
)
188-
await this.#bench.opts.teardown(this, 'run')
188+
await this.#bench.teardown(this, 'run')
189189

190190
this.#processRunResult({ error, latencySamples })
191191

@@ -209,19 +209,19 @@ export class Task extends EventTarget {
209209
this.#result = startedTaskResult
210210
this.dispatchEvent(new BenchEvent('start', this))
211211

212-
const setupResult = this.#bench.opts.setup(this, 'run')
212+
const setupResult = this.#bench.setup(this, 'run')
213213
invariant(
214214
!isPromiseLike(setupResult),
215215
'`setup` function must be sync when using `runSync()`'
216216
)
217217

218218
const { error, samples: latencySamples } = this.#benchmarkSync(
219219
'run',
220-
this.#bench.opts.time,
221-
this.#bench.opts.iterations
220+
this.#bench.time,
221+
this.#bench.iterations
222222
)
223223

224-
const teardownResult = this.#bench.opts.teardown(this, 'run')
224+
const teardownResult = this.#bench.teardown(this, 'run')
225225
invariant(
226226
!isPromiseLike(teardownResult),
227227
'`teardown` function must be sync when using `runSync()`'
@@ -241,13 +241,13 @@ export class Task extends EventTarget {
241241
return
242242
}
243243
this.dispatchEvent(new BenchEvent('warmup', this))
244-
await this.#bench.opts.setup(this, 'warmup')
244+
await this.#bench.setup(this, 'warmup')
245245
const { error } = (await this.#benchmark(
246246
'warmup',
247-
this.#bench.opts.warmupTime,
248-
this.#bench.opts.warmupIterations
247+
this.#bench.warmupTime,
248+
this.#bench.warmupIterations
249249
))
250-
await this.#bench.opts.teardown(this, 'warmup')
250+
await this.#bench.teardown(this, 'warmup')
251251

252252
this.#postWarmup(error)
253253
}
@@ -263,19 +263,19 @@ export class Task extends EventTarget {
263263

264264
this.dispatchEvent(new BenchEvent('warmup', this))
265265

266-
const setupResult = this.#bench.opts.setup(this, 'warmup')
266+
const setupResult = this.#bench.setup(this, 'warmup')
267267
invariant(
268268
!isPromiseLike(setupResult),
269269
'`setup` function must be sync when using `runSync()`'
270270
)
271271

272272
const { error } = this.#benchmarkSync(
273273
'warmup',
274-
this.#bench.opts.warmupTime,
275-
this.#bench.opts.warmupIterations
274+
this.#bench.warmupTime,
275+
this.#bench.warmupIterations
276276
)
277277

278-
const teardownResult = this.#bench.opts.teardown(this, 'warmup')
278+
const teardownResult = this.#bench.teardown(this, 'warmup')
279279
invariant(
280280
!isPromiseLike(teardownResult),
281281
'`teardown` function must be sync when using `runSync()`'
@@ -330,8 +330,8 @@ export class Task extends EventTarget {
330330
fn: benchmarkTask,
331331
iterations,
332332
limit: Math.max(1, Math.floor(this.#bench.threshold)),
333-
now: this.#bench.opts.now,
334-
signal: this.#signal ?? this.#bench.opts.signal,
333+
now: this.#bench.now,
334+
signal: this.#signal ?? this.#bench.signal,
335335
time,
336336
})
337337
} catch (error) {
@@ -444,10 +444,10 @@ export class Task extends EventTarget {
444444
}
445445

446446
async #measureOnce (): Promise<{ fnResult: ReturnType<Fn>, taskTime: number }> {
447-
const taskStart = this.#bench.opts.now()
447+
const taskStart = this.#bench.now()
448448
// eslint-disable-next-line no-useless-call
449449
const fnResult = await this.#fn.call(this)
450-
let taskTime = this.#bench.opts.now() - taskStart
450+
let taskTime = this.#bench.now() - taskStart
451451

452452
const overriddenDuration = getOverriddenDurationFromFnResult(fnResult)
453453
if (overriddenDuration !== undefined) {
@@ -457,10 +457,10 @@ export class Task extends EventTarget {
457457
}
458458

459459
#measureOnceSync (): { fnResult: ReturnType<Fn>, taskTime: number } {
460-
const taskStart = this.#bench.opts.now()
460+
const taskStart = this.#bench.now()
461461
// eslint-disable-next-line no-useless-call
462462
const fnResult = this.#fn.call(this)
463-
let taskTime = this.#bench.opts.now() - taskStart
463+
let taskTime = this.#bench.now() - taskStart
464464

465465
invariant(
466466
!isPromiseLike(fnResult),
@@ -493,7 +493,7 @@ export class Task extends EventTarget {
493493
const ev = new BenchEvent('error', this, error)
494494
this.dispatchEvent(ev)
495495
this.#bench.dispatchEvent(ev)
496-
if (this.#bench.opts.throws) {
496+
if (this.#bench.throws) {
497497
throw error
498498
}
499499
}
@@ -553,7 +553,7 @@ export class Task extends EventTarget {
553553
const ev = new BenchEvent('error', this, error)
554554
this.dispatchEvent(ev)
555555
this.#bench.dispatchEvent(ev)
556-
if (this.#bench.opts.throws) {
556+
if (this.#bench.throws) {
557557
throw error
558558
}
559559
}

0 commit comments

Comments
 (0)