Skip to content

Commit 1923023

Browse files
committed
Refactor benchmark to split out the frist run for in the incrimental runs
1 parent e241f67 commit 1923023

File tree

4 files changed

+490
-276
lines changed

4 files changed

+490
-276
lines changed

packages/d2ts-benchmark/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
"typecheck": "tsc --noEmit"
88
},
99
"devDependencies": {
10-
"@types/benchmark": "^2.1.5",
1110
"@types/better-sqlite3": "^7.6.12",
12-
"benchmark": "^2.1.4",
1311
"better-sqlite3": "^11.7.0",
12+
"table": "^6.8.1",
1413
"tsx": "^4.7.0",
1514
"typescript": "^5.0.0"
1615
},
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { table } from 'table'
2+
import type { TableUserConfig } from 'table'
3+
4+
export interface Benchmark<T> {
5+
name: string
6+
setup: () => T
7+
firstRun: (ctx: T) => void
8+
incrementalRun: (ctx: T, i: number) => void
9+
teardown?: (ctx: T) => void
10+
}
11+
12+
interface RunResult {
13+
startupTime: number
14+
firstRunTime: number
15+
incrementalTimes: number[]
16+
teardownTime: number
17+
}
18+
19+
export interface SuiteOptions {
20+
name: string
21+
totalRuns?: number
22+
incrementalRuns?: number
23+
warmupRuns?: number
24+
maxTimeDoingIncrementalRuns?: number
25+
}
26+
27+
export class Suite {
28+
name: string
29+
totalRuns: number
30+
incrementalRuns: number
31+
warmupRuns: number
32+
private benchmarks: Benchmark<unknown>[] = []
33+
private results: RunResult[] = []
34+
private maxTimeDoingIncrementalRuns?: number
35+
36+
constructor(options: SuiteOptions) {
37+
this.name = options.name
38+
this.totalRuns = options.totalRuns ?? 5
39+
this.incrementalRuns = options.incrementalRuns ?? 100
40+
this.warmupRuns = options.warmupRuns ?? 1
41+
this.maxTimeDoingIncrementalRuns = options.maxTimeDoingIncrementalRuns
42+
}
43+
44+
add<T>(benchmark: Benchmark<T>) {
45+
this.benchmarks.push(benchmark as Benchmark<unknown>)
46+
}
47+
48+
runTest(benchmark: Benchmark<unknown>): RunResult {
49+
const result: RunResult = {
50+
startupTime: 0,
51+
firstRunTime: 0,
52+
incrementalTimes: [],
53+
teardownTime: 0,
54+
}
55+
56+
let start = performance.now()
57+
const ctx = benchmark.setup()
58+
result.startupTime = performance.now() - start
59+
60+
start = performance.now()
61+
benchmark.firstRun(ctx)
62+
result.firstRunTime = performance.now() - start
63+
64+
const startedAt = performance.now()
65+
for (let i = 0; i < this.incrementalRuns; i++) {
66+
start = performance.now()
67+
benchmark.incrementalRun(ctx, i)
68+
result.incrementalTimes.push(performance.now() - start)
69+
if (
70+
this.maxTimeDoingIncrementalRuns &&
71+
performance.now() - startedAt > this.maxTimeDoingIncrementalRuns
72+
) {
73+
break
74+
}
75+
}
76+
77+
if (benchmark.teardown) {
78+
start = performance.now()
79+
benchmark.teardown(ctx)
80+
result.teardownTime = performance.now() - start
81+
}
82+
83+
return result
84+
}
85+
86+
run() {
87+
for (const benchmark of this.benchmarks) {
88+
for (let i = 0; i < this.warmupRuns; i++) {
89+
this.runTest(benchmark)
90+
}
91+
for (let i = 0; i < this.totalRuns; i++) {
92+
this.results.push(this.runTest(benchmark))
93+
}
94+
}
95+
}
96+
97+
printResults() {
98+
// Group results by benchmark
99+
const resultsByBenchmark = new Map<string, RunResult[]>()
100+
let i = 0
101+
for (const benchmark of this.benchmarks) {
102+
resultsByBenchmark.set(
103+
benchmark.name,
104+
this.results.slice(i * this.totalRuns, (i + 1) * this.totalRuns),
105+
)
106+
i++
107+
}
108+
109+
// Calculate data for the table
110+
const headers = [
111+
'Benchmark',
112+
'Startup (ms)',
113+
'First Run (ms)',
114+
'Incremental (ms)',
115+
'ops/sec',
116+
'Teardown (ms)',
117+
]
118+
const rows = Array.from(resultsByBenchmark.entries()).map(
119+
([name, results]) => {
120+
const avgIncrementalMs = results.map(
121+
(r) =>
122+
r.incrementalTimes.reduce((a, b) => a + b, 0) /
123+
r.incrementalTimes.length,
124+
)
125+
126+
return [
127+
name,
128+
formatAverage(results.map((r) => r.startupTime)),
129+
formatAverage(results.map((r) => r.firstRunTime)),
130+
formatAverage(avgIncrementalMs),
131+
formatOpsPerSec(avgIncrementalMs.map((ms) => 1000 / ms)),
132+
formatAverage(results.map((r) => r.teardownTime)),
133+
]
134+
},
135+
)
136+
137+
const data = [headers, ...rows]
138+
139+
const config: TableUserConfig = {
140+
columns: {
141+
0: { alignment: 'left' },
142+
1: { alignment: 'right' },
143+
2: { alignment: 'right' },
144+
3: { alignment: 'right' },
145+
4: { alignment: 'right' },
146+
5: { alignment: 'right' },
147+
},
148+
border: {
149+
topBody: `─`,
150+
topJoin: `┬`,
151+
topLeft: `┌`,
152+
topRight: `┐`,
153+
154+
bottomBody: `─`,
155+
bottomJoin: `┴`,
156+
bottomLeft: `└`,
157+
bottomRight: `┘`,
158+
159+
bodyLeft: `│`,
160+
bodyRight: `│`,
161+
bodyJoin: `│`,
162+
163+
joinBody: `─`,
164+
joinLeft: `├`,
165+
joinRight: `┤`,
166+
joinJoin: `┼`,
167+
},
168+
}
169+
170+
console.log(`\nResults for ${this.name}:`)
171+
console.log(table(data, config))
172+
}
173+
}
174+
175+
function formatAverage(numbers: number[]): string {
176+
const avg = numbers.reduce((a, b) => a + b, 0) / numbers.length
177+
return avg.toFixed(4)
178+
}
179+
180+
function formatOpsPerSec(numbers: number[]): string {
181+
const avg = Math.round(numbers.reduce((a, b) => a + b, 0) / numbers.length)
182+
return `${avg.toLocaleString('en-US')}`
183+
}

0 commit comments

Comments
 (0)