Dot performance dashboard
Warm, in-process timing of @knowvah/dot-engine vs the native dot oracle over the dot test corpus. Peer to PARITY.md — PARITY tracks correctness, PERF tracks speed. A report, not a gate. The fidelity target is ≤3× native.
Regenerate: npm run build:js && node test/corpus/bench.mjs && npm run bench:wasm && node test/corpus/perf-dashboard.mjs (the bench:wasm step is optional — it adds the dot-engine vs WASM section below).
Method
- Port: the shipped bundle (
dist/index.js) loaded once in a pool of resident, JIT-primed worker threads; purerenderSvg()is timed (best-of-N), so the measured region excludes all process/transpile/module-load startup — the warm steady state a long-lived consumer sees. Light graphs run at full pool; heavy graphs (native > 2s) are timed serially by default — measured cross-talk inflates a concurrent big render's single sample materially (≈66% on 2620). SetBENCH_HEAVY_POOL>1for a faster, noisier scan. - Native:
dot -Tsvgbest-of-3 (min). - Budget: target ≤3× native. Per-render cap 4800000ms (SIGKILL →
over-cap, i.e. a true synchronous hang). - Caveat: light graphs are timed under up-to-6-way load; for a precise single number re-run
BENCH_POOL=1 BENCH_IDS=<id> node test/corpus/bench.mjs.
Summary
- Rated inputs: 928 · within ≤3× native: 902 (97.2%)
- ok (≤3×): 902 · slow (>3×): 26 · over-cap (hang): 2 · errored: 0 · oracle-error: 9
- ratio (port/native): p50 0.02× · p90 0.70× · max 7.54×
Ratio distribution
| band | count |
|---|---|
| ≤1× (port ≥ native, warm) | 851 |
| 1–2× | 31 |
| 2–3× | 20 |
| 3–4× | 17 |
| 4–6× | 8 |
| 6–10× | 1 |
| >10× | 0 |
| over-cap (≥4800000ms, possible hang) | 2 |
Over budget — slower than 3× native (worst first)
| id | native ms | port ms (warm) | ratio |
|---|---|---|---|
2108 | 12849 | 96837 | 7.54× |
2471 | 3269 | 19162 | 5.86× |
graphs-b103 | 1210 | 6676 | 5.52× |
graphs-b100 | 8646 | 47617 | 5.51× |
2095 | 261 | 1216 | 4.66× |
2621 | 256000 | 1131355 | 4.42× |
linux.x86-root_circo | 245 | 1044 | 4.26× |
tree-contrib-dirgraph-bsdarch | 12 | 50 | 4.19× |
graphs-b104 | 8635 | 34857 | 4.04× |
nshare-root_circo | 247 | 988 | 4× |
graphs-root | 214 | 839 | 3.92× |
share-b29 | 578 | 2269 | 3.92× |
linux.x86-root_twopi | 248 | 947 | 3.82× |
nshare-root_twopi | 247 | 934 | 3.78× |
tree-contrib-dirgraph-linux | 8 | 30 | 3.75× |
2620 | 371 | 1382 | 3.73× |
2743 | 201 | 748 | 3.72× |
graphs-badvoro | 320 | 1183 | 3.7× |
2095_1 | 22309 | 82302 | 3.69× |
linux.i386-b29 | 582 | 2039 | 3.5× |
tree-doc-neato-jho | 11 | 38 | 3.45× |
tree-graphs-undirected-ngk10_4 | 15 | 51 | 3.43× |
graphs-b29 | 585 | 1989 | 3.4× |
1652 | 270306 | 894623 | 3.31× |
windows-b29 | 597 | 1939 | 3.25× |
tree-doc-dotguide-smlred | 21 | 65 | 3.11× |
Over-cap / possible hang
| id | native ms | cap ms | native×budget | status |
|---|---|---|---|---|
tree-share-examples-world | 1572090 | 4800000 | 4716270 | exceeds budget (likely hang/runaway) |
2475_1 | 1103343 | 4800000 | 3310029 | exceeds budget (likely hang/runaway) |
Raise BENCH_CAP_MS and re-run these ids to resolve an inconclusive status.
dot-engine vs WASM (@hpcc-js/wasm-graphviz)
A different axis from the table above: this compares the pure-TS port against the WASM build a browser would otherwise ship (@hpcc-js/wasm-graphviz@1.28.0) — both warm, in-process, best-of-3 (min). A report, not a gate: WASM is compiled C, so the port is expected to be a small constant factor slower; the port's wins are bundle size and no wasm fetch+instantiate, not raw layout compute. Source: wasm-perf.json. Regenerate: npm run bench:wasm.
- Compared: 765 inputs · ratio (dot-engine / wasm): p50 3.63× · p90 4.53× · max 7.60× · mean 3.58×
- Excluded: 12 heavy (native > 2000ms) · 4 large-source (> 1 MB) · 4 port-error · 7 wasm-error · 0 missing
Ratio distribution (dot-engine / wasm)
| band | count |
|---|---|
| dot-engine faster (<1×) | 1 |
| 1–2× | 31 |
| 2–4× | 509 |
| 4–6× | 214 |
| 6–10× | 10 |
| >10× | 0 |
Slowest for the port vs WASM (worst first)
| id | dot-engine ms | wasm ms | ratio |
|---|---|---|---|
graphs-Heawood | 6.113 | 0.804 | 7.604× |
2609 | 1.312 | 0.178 | 7.375× |
share-Heawood | 5.358 | 0.762 | 7.033× |
macosx-ordering_dot | 2.586 | 0.384 | 6.729× |
graphs-rd_rules | 3.613 | 0.553 | 6.53× |
graphs-grdfillcolor | 5.742 | 0.899 | 6.385× |
macosx-nestedclust_dot | 1.332 | 0.211 | 6.324× |
share-train11 | 5.148 | 0.826 | 6.232× |
macosx-ordering_dot1 | 2.413 | 0.388 | 6.222× |
graphs-sq_rules | 3.112 | 0.517 | 6.02× |
graphs-pslib | 5.571 | 0.929 | 5.998× |
graphs-ER | 2.501 | 0.421 | 5.94× |
graphs-b786 | 2.078 | 0.36 | 5.775× |
share-ER | 2.709 | 0.47 | 5.763× |
graphs-grdshapes | 6.117 | 1.079 | 5.667× |
graphs-grdlinear | 5.721 | 1.018 | 5.62× |
share-trapeziumlr | 8.139 | 1.462 | 5.566× |
121 | 2.463 | 0.444 | 5.544× |
share-labelclust-nbl | 0.982 | 0.179 | 5.48× |
1408 | 1.092 | 0.2 | 5.455× |
Excluded / not compared
- Heavy (native > 2000ms): 12 inputs — warm best-of-N over multi-second layouts is impractical here; their vs-native behavior is in the main table above.
- Large source (> 1 MB):
1864(14.1 MB),2064(4.9 MB),2475_1(9.3 MB),2593(13.2 MB) — multi-MB disassembly/CFG dumps (~10k+ nodes) that are impractical for a warm best-of-N. These are hard for Graphviz generally, not port-specific: spot-checked,2064fails to render in nativedot(>120s), the WASM build (>60s inlayout()), and the port (>30s) alike;1864OOMs both the port (>1 GB) and the WASM build ("out of memory") while native times out. The port does not leak (with GC its per-render heap is flat ~17 MB) — this is a scale ceiling shared with upstream, not a defect. - Per-side error ids are listed in
wasm-perf.json.