60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
export type Workload = {
|
|
run(index: number): void
|
|
consume(): number
|
|
dispose?(): void
|
|
}
|
|
|
|
export type Variant = {
|
|
readonly name: string
|
|
readonly make: () => Workload
|
|
}
|
|
|
|
export function createHarness(options: { readonly samples?: number; readonly warmup?: number } = {}) {
|
|
const samples = options.samples ?? 9
|
|
const warmup = options.warmup ?? 500
|
|
let checksum = 0
|
|
|
|
return {
|
|
samples,
|
|
compare(iterations: number, variants: readonly Variant[]) {
|
|
const timings = variants.map(() => [] as number[])
|
|
for (let sample = -1; sample < samples; sample++) {
|
|
const offset = sample < 0 ? 0 : sample % variants.length
|
|
variants
|
|
.map((_variant, index) => (index + offset) % variants.length)
|
|
.forEach((variantIndex) => {
|
|
const workload = variants[variantIndex].make()
|
|
for (let index = 0; index < Math.min(iterations, warmup); index++) workload.run(index)
|
|
const start = Bun.nanoseconds()
|
|
for (let index = 0; index < iterations; index++) workload.run(index)
|
|
const elapsed = Bun.nanoseconds() - start
|
|
checksum += workload.consume()
|
|
workload.dispose?.()
|
|
if (sample >= 0) timings[variantIndex].push(elapsed / iterations)
|
|
})
|
|
}
|
|
|
|
const medians = variants.map((variant, index) => {
|
|
const median = middle(timings[index])
|
|
const mad = middle(timings[index].map((value) => Math.abs(value - median)))
|
|
const metric = variant.name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "_")
|
|
console.log(`${variant.name.padEnd(42)} ${median.toFixed(1).padStart(10)} ns/op +/- ${mad.toFixed(1)} MAD`)
|
|
console.log(`METRIC ${metric}_ns_per_op=${median.toFixed(3)}`)
|
|
return median
|
|
})
|
|
return {
|
|
medians,
|
|
ratio(left: number, right: number) {
|
|
return middle(timings[left].map((value, index) => value / timings[right][index]))
|
|
},
|
|
}
|
|
},
|
|
finish() {
|
|
console.log(`CHECKSUM ${checksum}`)
|
|
},
|
|
}
|
|
}
|
|
|
|
function middle(values: number[]) {
|
|
return values.toSorted((a, b) => a - b)[Math.floor(values.length / 2)]
|
|
}
|