Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 42 additions & 29 deletions src/bench.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import type {
AddEventListenerOptionsArgument,
BenchEvents,
BenchLike,
BenchOptions,
EventListener,
EventListenerObject,
Fn,
FnOptions,
RemoveEventListenerOptionsArgument,
ResolvedBenchOptions,
TaskResult,
} from './types'

import {
defaultMinimumIterations,
defaultMinimumTime,
defaultMinimumWarmupIterations,
defaultMinimumIterations as defaultIterations,
defaultMinimumWarmupTime,
defaultMinimumTime as defaultTime,
defaultMinimumWarmupIterations as defaultWarmupIterations,
emptyFunction,
} from './constants'
import { BenchEvent } from './event'
Expand All @@ -32,7 +32,7 @@ import {
/**
* The Bench class keeps track of the benchmark tasks and controls them.
*/
export class Bench extends EventTarget {
export class Bench extends EventTarget implements BenchLike {
declare addEventListener: <K extends BenchEvents>(
type: K,
listener: EventListener<K> | EventListenerObject<K> | null,
Expand All @@ -46,17 +46,16 @@ export class Bench extends EventTarget {
* - When `mode` is set to 'task', each task's iterations (calls of a task function) run concurrently.
* - When `mode` is set to 'bench', different tasks within the bench run concurrently.
*/
concurrency: 'bench' | 'task' | null = null
readonly concurrency: 'bench' | 'task' | null = null

readonly iterations: number

/**
* The benchmark name.
*/
readonly name: string | undefined

/**
* The options.
*/
readonly opts: Readonly<ResolvedBenchOptions>
readonly now: () => number

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

readonly setup: (task: Task, mode: 'run' | 'warmup') => Promise<void> | void

readonly signal: AbortSignal | undefined

readonly teardown: (task: Task, mode: 'run' | 'warmup') => Promise<void> | void

/**
* The maximum number of concurrent tasks to run
* @default Infinity
*/
threshold = Infinity
readonly threshold = Infinity

readonly throws: boolean

readonly time: number

readonly warmup: boolean

readonly warmupIterations: number

readonly warmupTime: number

/**
* tasks results as an array
Expand Down Expand Up @@ -110,23 +125,21 @@ export class Bench extends EventTarget {
this.concurrency = restOptions.concurrency ?? null
this.threshold = restOptions.threshold ?? Infinity

this.opts = {
...{
iterations: defaultMinimumIterations,
now: performanceNow,
setup: emptyFunction,
teardown: emptyFunction,
throws: false,
time: defaultMinimumTime,
warmup: true,
warmupIterations: defaultMinimumWarmupIterations,
warmupTime: defaultMinimumWarmupTime,
},
...restOptions,
}
this.time = restOptions.time ?? defaultTime
this.iterations = restOptions.iterations ?? defaultIterations
this.now = restOptions.now ?? performanceNow
this.warmup = restOptions.warmup ?? true
this.warmupIterations =
restOptions.warmupIterations ?? defaultWarmupIterations
this.warmupTime =
restOptions.warmupTime ?? defaultMinimumWarmupTime
this.setup = restOptions.setup ?? emptyFunction
this.teardown = restOptions.teardown ?? emptyFunction
this.throws = restOptions.throws ?? false
this.signal = restOptions.signal

if (this.opts.signal) {
this.opts.signal.addEventListener(
if (this.signal) {
this.signal.addEventListener(
'abort',
() => {
this.dispatchEvent(new BenchEvent('abort'))
Expand Down Expand Up @@ -193,7 +206,7 @@ export class Bench extends EventTarget {
* @returns the tasks array
*/
async run (): Promise<Task[]> {
if (this.opts.warmup) {
if (this.warmup) {
await this.#warmupTasks()
}

Expand Down Expand Up @@ -226,7 +239,7 @@ export class Bench extends EventTarget {
this.concurrency === null,
'Cannot use `concurrency` option when using `runSync`'
)
if (this.opts.warmup) {
if (this.warmup) {
this.#warmupTasksSync()
}
const values: Task[] = []
Expand Down
60 changes: 30 additions & 30 deletions src/task.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Bench } from './bench'
import type {
AddEventListenerOptionsArgument,
BenchLike,
EventListener,
EventListenerObject,
Fn,
Expand Down Expand Up @@ -79,7 +79,7 @@ export class Task extends EventTarget {
/**
* The Bench instance reference
*/
readonly #bench: Bench
readonly #bench: BenchLike

/**
* The task function
Expand Down Expand Up @@ -111,7 +111,7 @@ export class Task extends EventTarget {
*/
readonly #signal: AbortSignal | undefined

constructor (bench: Bench, name: string, fn: Fn, fnOpts: FnOptions = {}) {
constructor (bench: BenchLike, name: string, fn: Fn, fnOpts: FnOptions = {}) {
super()
this.#bench = bench
this.#name = name
Expand Down Expand Up @@ -143,11 +143,11 @@ export class Task extends EventTarget {
}
}

if (this.#bench.opts.signal) {
if (this.#bench.opts.signal.aborted) {
if (this.#bench.signal) {
if (this.#bench.signal.aborted) {
this.#onAbort()
} else {
this.#bench.opts.signal.addEventListener(
this.#bench.signal.addEventListener(
'abort',
this.#onAbort.bind(this),
{ once: true }
Expand Down Expand Up @@ -179,13 +179,13 @@ export class Task extends EventTarget {
}
this.#result = { state: 'started' }
this.dispatchEvent(new BenchEvent('start', this))
await this.#bench.opts.setup(this, 'run')
await this.#bench.setup(this, 'run')
const { error, samples: latencySamples } = await this.#benchmark(
'run',
this.#bench.opts.time,
this.#bench.opts.iterations
this.#bench.time,
this.#bench.iterations
)
await this.#bench.opts.teardown(this, 'run')
await this.#bench.teardown(this, 'run')

this.#processRunResult({ error, latencySamples })

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

const setupResult = this.#bench.opts.setup(this, 'run')
const setupResult = this.#bench.setup(this, 'run')
invariant(
!isPromiseLike(setupResult),
'`setup` function must be sync when using `runSync()`'
)

const { error, samples: latencySamples } = this.#benchmarkSync(
'run',
this.#bench.opts.time,
this.#bench.opts.iterations
this.#bench.time,
this.#bench.iterations
)

const teardownResult = this.#bench.opts.teardown(this, 'run')
const teardownResult = this.#bench.teardown(this, 'run')
invariant(
!isPromiseLike(teardownResult),
'`teardown` function must be sync when using `runSync()`'
Expand All @@ -241,13 +241,13 @@ export class Task extends EventTarget {
return
}
this.dispatchEvent(new BenchEvent('warmup', this))
await this.#bench.opts.setup(this, 'warmup')
await this.#bench.setup(this, 'warmup')
const { error } = (await this.#benchmark(
'warmup',
this.#bench.opts.warmupTime,
this.#bench.opts.warmupIterations
this.#bench.warmupTime,
this.#bench.warmupIterations
))
await this.#bench.opts.teardown(this, 'warmup')
await this.#bench.teardown(this, 'warmup')

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

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

const setupResult = this.#bench.opts.setup(this, 'warmup')
const setupResult = this.#bench.setup(this, 'warmup')
invariant(
!isPromiseLike(setupResult),
'`setup` function must be sync when using `runSync()`'
)

const { error } = this.#benchmarkSync(
'warmup',
this.#bench.opts.warmupTime,
this.#bench.opts.warmupIterations
this.#bench.warmupTime,
this.#bench.warmupIterations
)

const teardownResult = this.#bench.opts.teardown(this, 'warmup')
const teardownResult = this.#bench.teardown(this, 'warmup')
invariant(
!isPromiseLike(teardownResult),
'`teardown` function must be sync when using `runSync()`'
Expand Down Expand Up @@ -330,8 +330,8 @@ export class Task extends EventTarget {
fn: benchmarkTask,
iterations,
limit: Math.max(1, Math.floor(this.#bench.threshold)),
now: this.#bench.opts.now,
signal: this.#signal ?? this.#bench.opts.signal,
now: this.#bench.now,
signal: this.#signal ?? this.#bench.signal,
time,
})
} catch (error) {
Expand Down Expand Up @@ -444,10 +444,10 @@ export class Task extends EventTarget {
}

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

const overriddenDuration = getOverriddenDurationFromFnResult(fnResult)
if (overriddenDuration !== undefined) {
Expand All @@ -457,10 +457,10 @@ export class Task extends EventTarget {
}

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

invariant(
!isPromiseLike(fnResult),
Expand Down Expand Up @@ -493,7 +493,7 @@ export class Task extends EventTarget {
const ev = new BenchEvent('error', this, error)
this.dispatchEvent(ev)
this.#bench.dispatchEvent(ev)
if (this.#bench.opts.throws) {
if (this.#bench.throws) {
throw error
}
}
Expand Down Expand Up @@ -553,7 +553,7 @@ export class Task extends EventTarget {
const ev = new BenchEvent('error', this, error)
this.dispatchEvent(ev)
this.#bench.dispatchEvent(ev)
if (this.#bench.opts.throws) {
if (this.#bench.throws) {
throw error
}
}
Expand Down
Loading
Loading