meat/scripts/bench-browser.ts

94 lines
3 KiB
TypeScript
Raw Normal View History

2026-08-05 09:24:13 +02:00
import { chromium } from "playwright"
// Start the Vite dev server (it serves the COOP/COEP headers needed for
// SharedArrayBuffer), drive real headless Chromium through both bench modes, and
// read the timings back. Real V8 + Web Workers + SAB + rAF -- the environment my
// headless Bun benches can't see.
const PORT = 5199
const URL = `http://localhost:${PORT}`
const vite = Bun.spawn(["bunx", "--bun", "vite", "--port", String(PORT), "--strictPort"], {
cwd: import.meta.dir + "/..",
stdout: "pipe",
stderr: "pipe",
env: { ...process.env, NO_COLOR: "1" },
})
async function waitForServer(): Promise<void> {
for (let i = 0; i < 150; i++) {
try {
const r = await fetch(URL + "/")
if (r.ok) {
return
}
} catch {
// not up yet
}
await Bun.sleep(200)
}
throw new Error("vite dev server did not start")
}
type Bench = {
mode: string
parallel: boolean
cores: number
coi: boolean
res: string
workMs: { median: number; p95: number; max: number; mean: number }
frameMs: { median: number; p95: number; max: number; mean: number }
}
async function main(): Promise<void> {
await waitForServer()
const browser = await chromium.launch({
headless: true,
args: [
"--enable-features=SharedArrayBuffer",
"--disable-background-timer-throttling",
"--disable-renderer-backgrounding",
"--disable-backgrounding-occluded-windows",
],
})
async function run(mode: string): Promise<Bench> {
const page = await browser.newPage()
page.on("pageerror", (e) => console.log(` [page error] ${e.message}`))
page.on("console", (m) => {
const t = m.text()
if (t.startsWith("BENCH") || m.type() === "error") {
console.log(` [console] ${t}`)
}
})
await page.setViewportSize({ width: 800, height: 600 })
await page.goto(`${URL}/?bench=${mode}`, { waitUntil: "load" })
await page.waitForFunction("window.__BENCH__ !== undefined", null, { timeout: 120000 })
const result = (await page.evaluate("window.__BENCH__")) as Bench
await page.close()
return result
}
console.log("--- single-thread ---")
const st = await run("st")
console.log("--- workers ---")
const mt = await run("mt")
await browser.close()
const line = (b: Bench) =>
`parallel=${b.parallel} coi=${b.coi} cores=${b.cores} res=${b.res} work med/p95/max = ${b.workMs.median}/${b.workMs.p95}/${b.workMs.max} ms frame med/p95 = ${b.frameMs.median}/${b.frameMs.p95} ms`
console.log("")
console.log(`single-thread : ${line(st)}`)
console.log(`workers : ${line(mt)}`)
console.log("")
console.log(`work median : ${st.workMs.median} -> ${mt.workMs.median} ms (${(st.workMs.median / mt.workMs.median).toFixed(2)}x)`)
console.log(`work p95 : ${st.workMs.p95} -> ${mt.workMs.p95} ms (${(st.workMs.p95 / mt.workMs.p95).toFixed(2)}x)`)
console.log(`frame p95 : ${st.frameMs.p95} -> ${mt.frameMs.p95} ms (jitter: lower p95/median = steadier)`)
}
try {
await main()
} finally {
vite.kill()
}