54 lines
1.5 KiB
JavaScript
54 lines
1.5 KiB
JavaScript
|
|
import { performance } from 'node:perf_hooks'
|
||
|
|
|
||
|
|
function percentile(sorted, p) {
|
||
|
|
if (sorted.length === 0) return 0
|
||
|
|
const idx = Math.min(sorted.length - 1, Math.floor(sorted.length * p))
|
||
|
|
return sorted[idx]
|
||
|
|
}
|
||
|
|
|
||
|
|
export function getBenchOptions() {
|
||
|
|
const runs = Number.parseInt(process.env.BENCH_RUNS ?? '8', 10)
|
||
|
|
const warmup = Number.parseInt(process.env.BENCH_WARMUP ?? '2', 10)
|
||
|
|
return {
|
||
|
|
runs: Number.isFinite(runs) && runs > 1 ? runs : 8,
|
||
|
|
warmup: Number.isFinite(warmup) && warmup >= 0 ? warmup : 2,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function measure(name, fn, options) {
|
||
|
|
const times = []
|
||
|
|
|
||
|
|
for (let i = 0; i < options.runs; i++) {
|
||
|
|
const t0 = performance.now()
|
||
|
|
await fn()
|
||
|
|
const dt = performance.now() - t0
|
||
|
|
if (i >= options.warmup) {
|
||
|
|
times.push(dt)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
times.sort((a, b) => a - b)
|
||
|
|
const mean = times.reduce((sum, value) => sum + value, 0) / Math.max(1, times.length)
|
||
|
|
|
||
|
|
return {
|
||
|
|
name,
|
||
|
|
samples: times.length,
|
||
|
|
mean,
|
||
|
|
min: times[0] ?? 0,
|
||
|
|
p50: percentile(times, 0.5),
|
||
|
|
p95: percentile(times, 0.95),
|
||
|
|
max: times[times.length - 1] ?? 0,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function printResults(title, results, options) {
|
||
|
|
console.log(`\n${title}`)
|
||
|
|
console.log(`runs=${options.runs} warmup=${options.warmup} measured=${Math.max(0, options.runs - options.warmup)}`)
|
||
|
|
for (const row of results) {
|
||
|
|
console.log(
|
||
|
|
`${row.name.padEnd(32)} n=${String(row.samples).padStart(2)} mean=${row.mean.toFixed(1)}ms ` +
|
||
|
|
`p50=${row.p50.toFixed(1)}ms p95=${row.p95.toFixed(1)}ms min=${row.min.toFixed(1)}ms max=${row.max.toFixed(1)}ms`
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|