assets/mashup_forge_bakeoff.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1180 1181" font-family="'DejaVu Sans', 'Segoe UI', sans-serif">
<!-- GENERATED. Do not hand-edit: every number here carries a measurement, and
editing the picture instead of the datum is how a diagram starts lying. -->
<defs>
<linearGradient id="board" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0b1c2e"/><stop offset="1" stop-color="#06111d"/>
</linearGradient>
<linearGradient id="chip" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#14384f"/><stop offset="1" stop-color="#0e2a3e"/>
</linearGradient>
<linearGradient id="rail" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#173d57"/><stop offset="1" stop-color="#0c2438"/>
</linearGradient>
<linearGradient id="slot" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#123448"/><stop offset="1" stop-color="#0a2032"/>
</linearGradient>
<linearGradient id="hot" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#1c5a3a"/><stop offset="1" stop-color="#0f3a26"/>
</linearGradient>
<linearGradient id="cold" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#3a2320"/><stop offset="1" stop-color="#281414"/>
</linearGradient>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M0 0 L10 5 L0 10 z" fill="context-stroke"/>
</marker>
</defs>
<rect x="0" y="0" width="1180" height="1181" fill="url(#board)"/>
<g stroke="#143a52" stroke-width="1">
<line x1="0" y1="76" x2="1180" y2="76"/>
<line x1="0" y1="1133" x2="1180" y2="1133"/>
</g>
<g fill="#16415c">
<circle cx="40" cy="40" r="2"/><circle cx="1140" cy="40" r="2"/>
<circle cx="40" cy="1153" r="2"/><circle cx="1140" cy="1153" r="2"/>
</g>
<text x="40" y="44" fill="#eaf6ff" font-size="22" font-weight="700">gunnar vs gitea vs Forgejo</text>
<text x="407.2" y="38" fill="#9fc9e0" font-size="13">stock git is the client for all three · same box, same fixture, one counting relay</text>
<text x="407.2" y="56" fill="#7fa8c4" font-size="10.5">iceberg bench_runs · the desktop · 32 cores · 2026-08-13T10:18:18.038867+00:00 · 3 interleaved rounds · loadavg 4.39</text>
<!-- ==================== -->
<!-- PANEL 1 - one bar group per operation, two bars each -->
<text x="40" y="100" fill="#6fd3f5" font-size="15" text-anchor="start" font-family="'DejaVu Sans Mono', monospace" font-weight="700">1</text>
<text x="74" y="100" fill="#eaf6ff" font-size="15" text-anchor="start" font-weight="700">Bytes on the wire, and what the operation cost the box</text>
<text x="74" y="117" fill="#9fc9e0" font-size="11" text-anchor="start">bars are bytes · the line under them is net CPU s and peak RSS · green = gunnar spends less, coral = gunnar spends more</text>
<line x1="40" y1="126" x2="1140" y2="126" stroke="#143a52" stroke-width="1"/>
<text x="40" y="146" fill="#eaf6ff" font-size="13" text-anchor="start" font-family="'DejaVu Sans Mono', monospace" font-weight="700">clone</text>
<text x="122" y="146" fill="#9fc9e0" font-size="9.5" text-anchor="start">pack copy against git's own pack-objects · pack and protocol, server to client</text>
<text x="96" y="170" fill="#eaf6ff" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gunnar</text>
<rect x="102" y="158" width="649.02" height="16" rx="3" fill="url(#chip)" stroke="#bfe6ff" stroke-width="1.2"/>
<text x="759.02" y="170" fill="#bfe6ff" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">1.78 MB</text>
<text x="96" y="192" fill="#9fc9e0" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gitea + Forgejo</text>
<rect x="102" y="180" width="650" height="16" rx="3" fill="url(#rail)" stroke="#4a6f88" stroke-width="1.2"/>
<text x="760" y="192" fill="#86b3cc" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">1.78 MB</text>
<text x="1140" y="170" fill="#bfe6ff" font-size="11" text-anchor="end" font-weight="700">tie, within 2% — 0.9985x</text>
<text x="1140" y="210" fill="#5f8ba6" font-size="8.8" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">small · 3 257 objects · other corpora: large 0.9964x, real 1.0005x, xl 0.9966x, year 0.9999x · ms 72/84</text>
<text x="102" y="224" fill="#8fe6b8" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">net CPU s 0.010 vs 0.033 3.42x less CPU</text>
<text x="432" y="224" fill="#f0a9a0" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">peak RSS MB 2308 vs 113 20.49x more RSS</text>
<text x="40" y="252" fill="#eaf6ff" font-size="13" text-anchor="start" font-family="'DejaVu Sans Mono', monospace" font-weight="700">fetch</text>
<text x="122" y="252" fill="#9fc9e0" font-size="9.5" text-anchor="start">no server-side delta compression — S-003 · the answer to a negotiation, server to client</text>
<text x="96" y="276" fill="#eaf6ff" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gunnar</text>
<rect x="102" y="264" width="650" height="16" rx="3" fill="url(#cold)" stroke="#f0a9a0" stroke-width="1.2"/>
<text x="760" y="276" fill="#f0a9a0" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">344.0 kB</text>
<text x="96" y="298" fill="#9fc9e0" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gitea + Forgejo</text>
<rect x="102" y="286" width="560.49" height="16" rx="3" fill="url(#rail)" stroke="#4a6f88" stroke-width="1.2"/>
<text x="670.49" y="298" fill="#86b3cc" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">296.6 kB</text>
<text x="1140" y="276" fill="#f0a9a0" font-size="11" text-anchor="end" font-weight="700">gunnar 1.16x more bytes</text>
<text x="1140" y="316" fill="#5f8ba6" font-size="8.8" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">diverge · 2 912 objects · one corpus measured · ms 90/93</text>
<text x="102" y="330" fill="#8fe6b8" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">net CPU s 0.037 vs 0.055 1.47x less CPU</text>
<text x="432" y="330" fill="#f0a9a0" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">peak RSS MB 156 vs 109 1.44x more RSS</text>
<text x="40" y="358" fill="#eaf6ff" font-size="13" text-anchor="start" font-family="'DejaVu Sans Mono', monospace" font-weight="700">push</text>
<text x="122" y="358" fill="#9fc9e0" font-size="9.5" text-anchor="start">the write path; gunnar does not run git's packer · the server's response to 2 MB in</text>
<text x="96" y="382" fill="#eaf6ff" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gunnar</text>
<rect x="102" y="370" width="448.5" height="16" rx="3" fill="url(#hot)" stroke="#8fe6b8" stroke-width="1.2"/>
<text x="558.5" y="382" fill="#8fe6b8" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">1.4 kB</text>
<text x="96" y="404" fill="#9fc9e0" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gitea + Forgejo</text>
<rect x="102" y="392" width="650" height="16" rx="3" fill="url(#rail)" stroke="#4a6f88" stroke-width="1.2"/>
<text x="760" y="404" fill="#86b3cc" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">2.1 kB</text>
<text x="1140" y="382" fill="#8fe6b8" font-size="11" text-anchor="end" font-weight="700">gunnar 1.45x fewer bytes</text>
<text x="1140" y="422" fill="#5f8ba6" font-size="8.8" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">base · 0 objects · one corpus measured · ms 340/821</text>
<text x="102" y="436" fill="#8fe6b8" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">net CPU s 0.302 vs 0.944 3.13x less CPU</text>
<text x="432" y="436" fill="#f0a9a0" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">peak RSS MB 620 vs 125 4.95x more RSS</text>
<text x="40" y="464" fill="#eaf6ff" font-size="13" text-anchor="start" font-family="'DejaVu Sans Mono', monospace" font-weight="700">ls-remote</text>
<text x="122" y="464" fill="#9fc9e0" font-size="9.5" text-anchor="start">the smallest request in the protocol, and no pack · the ref advertisement</text>
<text x="96" y="488" fill="#eaf6ff" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gunnar</text>
<rect x="102" y="476" width="623.25" height="16" rx="3" fill="url(#hot)" stroke="#8fe6b8" stroke-width="1.2"/>
<text x="733.25" y="488" fill="#8fe6b8" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">1.2 kB</text>
<text x="96" y="510" fill="#9fc9e0" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gitea + Forgejo</text>
<rect x="102" y="498" width="650" height="16" rx="3" fill="url(#rail)" stroke="#4a6f88" stroke-width="1.2"/>
<text x="760" y="510" fill="#86b3cc" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">1.2 kB</text>
<text x="1140" y="488" fill="#8fe6b8" font-size="11" text-anchor="end" font-weight="700">gunnar 1.04x fewer bytes</text>
<text x="1140" y="528" fill="#5f8ba6" font-size="8.8" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">small · 0 objects · other corpora: real 0.9879x, year 0.9932x · ms 9/12</text>
<text x="102" y="542" fill="#8fe6b8" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">net CPU s 0.001 vs 0.004 6.29x less CPU</text>
<text x="432" y="542" fill="#f0a9a0" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">peak RSS MB 331 vs 144 2.30x more RSS</text>
<text x="40" y="570" fill="#eaf6ff" font-size="13" text-anchor="start" font-family="'DejaVu Sans Mono', monospace" font-weight="700">clone, ramped</text>
<text x="151.74" y="570" fill="#9fc9e0" font-size="9.5" text-anchor="start">cost per operation at N clients, not at one · one batch of N concurrent clients</text>
<text x="96" y="594" fill="#eaf6ff" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gunnar</text>
<rect x="102" y="582" width="650" height="16" rx="3" fill="url(#chip)" stroke="#bfe6ff" stroke-width="1.2"/>
<text x="760" y="594" fill="#bfe6ff" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">392.97 MB</text>
<text x="96" y="616" fill="#9fc9e0" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gitea + Forgejo</text>
<rect x="102" y="604" width="649.76" height="16" rx="3" fill="url(#rail)" stroke="#4a6f88" stroke-width="1.2"/>
<text x="759.76" y="616" fill="#86b3cc" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">392.83 MB</text>
<text x="1140" y="594" fill="#bfe6ff" font-size="11" text-anchor="end" font-weight="700">tie, within 2% — 1.0004x</text>
<text x="1140" y="634" fill="#5f8ba6" font-size="8.8" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">c16 · 462 288 objects · other corpora: c1 0.9997x, c1-pin 0.9999x, c16-pin 1.0004x, c4 0.9999x, c4-pin 0.9999x · ms 1226/1127</text>
<text x="102" y="648" fill="#8fe6b8" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">net CPU s 3.179 vs 3.956 1.24x less CPU</text>
<text x="432" y="648" fill="#f0a9a0" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">peak RSS MB 1004 vs 172 5.85x more RSS</text>
<text x="40" y="676" fill="#eaf6ff" font-size="13" text-anchor="start" font-family="'DejaVu Sans Mono', monospace" font-weight="700">clone, ramped</text>
<text x="151.74" y="676" fill="#9fc9e0" font-size="9.5" text-anchor="start">cost per operation at N clients, not at one · one batch of N concurrent clients</text>
<text x="96" y="700" fill="#eaf6ff" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gunnar</text>
<rect x="102" y="688" width="650" height="16" rx="3" fill="url(#chip)" stroke="#bfe6ff" stroke-width="1.2"/>
<text x="760" y="700" fill="#bfe6ff" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">392.97 MB</text>
<text x="96" y="722" fill="#9fc9e0" font-size="9.5" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">gitea + Forgejo</text>
<rect x="102" y="710" width="649.75" height="16" rx="3" fill="url(#rail)" stroke="#4a6f88" stroke-width="1.2"/>
<text x="759.75" y="722" fill="#86b3cc" font-size="9.4" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">392.82 MB</text>
<text x="1140" y="700" fill="#bfe6ff" font-size="11" text-anchor="end" font-weight="700">tie, within 2% — 1.0004x</text>
<text x="1140" y="740" fill="#5f8ba6" font-size="8.8" text-anchor="end" font-family="'DejaVu Sans Mono', monospace">c16-pin · 462 288 objects · other corpora: c1 0.9997x, c1-pin 0.9999x, c16 1.0004x, c4 0.9999x, c4-pin 0.9999x · ms 1118/1186</text>
<text x="102" y="754" fill="#8fe6b8" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">net CPU s 2.290 vs 3.814 1.67x less CPU</text>
<text x="432" y="754" fill="#f0a9a0" font-size="9.6" text-anchor="start" font-family="'DejaVu Sans Mono', monospace">peak RSS MB 924 vs 115 8.05x more RSS</text>
<rect x="40" y="788" width="1100" height="63" rx="8" fill="url(#rail)" stroke="#6fd3f5" stroke-width="2"/>
<text x="56" y="809" fill="#a7e6c2" font-size="12" text-anchor="start" font-weight="700">Conditions these bytes were produced under, read off the same rows</text>
<text x="56" y="824" fill="#bcd9ec" font-size="9.5" text-anchor="start">3 interleaved rounds · loadavg 4.39 at the end of the run · device read-ahead 2048 kB · gunnar cgroup 8.00 cpus / 8192 MB · gitea cgroup 8.00 cpus / 8192 MB · forgejo cgroup 8.00 cpus / 8192 MB ·</text>
<text x="56" y="836.5" fill="#bcd9ec" font-size="9.5" text-anchor="start">znippy cold tier not wired — gunnar on the filesystem</text>
<rect x="40" y="861" width="1100" height="88" rx="8" fill="url(#rail)" stroke="#f0a9a0" stroke-width="2" stroke-dasharray="3 4"/>
<text x="56" y="882" fill="#f0a9a0" font-size="12" text-anchor="start" font-weight="700">What these bars do not show</text>
<text x="56" y="897" fill="#bcd9ec" font-size="9.5" text-anchor="start">Forgejo is a gitea fork. Under `podman top` during a clone both containers exec the same `/usr/bin/git-upload-pack` → `git pack-objects --revs --thin --stdout --delta-base-offset --include-tag`, so</text>
<text x="56" y="909.5" fill="#bcd9ec" font-size="9.5" text-anchor="start">drawing both would be one Go engine drawn twice; the rival bar is whichever of the two sent fewer bytes. gunnar's server process spawned no children under the same probe. · The znippy cold tier was not</text>
<text x="56" y="922" fill="#bcd9ec" font-size="9.5" text-anchor="start">running: `pack_tier` returns an empty vector, `RepackOptions::cold_tier()` (crates/gunnar-store/src/repack.rs:277) has no call sites in any server crate, and the control plane prints: no znippy cold tier is</text>
<text x="56" y="934.5" fill="#bcd9ec" font-size="9.5" text-anchor="start">built. Objects are gix-odb packs on disk, the same place gitea's are. No bar above speaks to the tier; the arm records that in its `znippy_wired` column.</text>
<rect x="40" y="959" width="1100" height="100.5" rx="8" fill="url(#rail)" stroke="#6fd3f5" stroke-width="2"/>
<text x="56" y="980" fill="#a7e6c2" font-size="12" text-anchor="start" font-weight="700">Method</text>
<text x="56" y="995" fill="#bcd9ec" font-size="9.5" text-anchor="start">Client: stock git for all three servers, so no engine is measured through its own client; the version it ran is a column on every row. Fixture: real history off this box, pushed to all three from one bare</text>
<text x="56" y="1007.5" fill="#bcd9ec" font-size="9.5" text-anchor="start">repo; tip oid and each clone's object set are compared against the fixture before anything is scored, and the object counts beside each group are that check. Instrument: the counting relay in `cargo xtask</text>
<text x="56" y="1020" fill="#bcd9ec" font-size="9.5" text-anchor="start">wireproxy`, in front of every engine, same read/write loop. Order: within a round the three engines run back to back on one fixture. Quotas: `parity.rs` reads each container's own cgroup back and aborts</text>
<text x="56" y="1032.5" fill="#bcd9ec" font-size="9.5" text-anchor="start">unless the quotas that applied match; those read-back figures are the conditions row. A server that never started, one serving the wrong repository, or a relay that stopped counting aborts the run instead</text>
<text x="56" y="1045" fill="#bcd9ec" font-size="9.5" text-anchor="start">of scoring a zero. Recipe `.nornir/bench/forge-bakeoff.sh`, arms in `bench/examples/forge/`.</text>
<rect x="40" y="1069.5" width="1100" height="75.5" rx="8" fill="url(#rail)" stroke="#e0a86a" stroke-width="2" stroke-dasharray="7 5"/>
<text x="56" y="1090.5" fill="#9fc9e0" font-size="12" text-anchor="start" font-weight="700">Arms with no row in this run</text>
<text x="56" y="1105.5" fill="#bcd9ec" font-size="9.5" text-anchor="start">vs_forge_fetch_gc, vs_forge_gc_orders, vs_forge_mixed — NOT MEASURED in this run, and not substituted. The bake-off arms are heavy container arms and are not in every run. To put rows in the table, build</text>
<text x="56" y="1118" fill="#bcd9ec" font-size="9.5" text-anchor="start">gunnar's server with the http feature into the bench target dir and run the arms: `CARGO_TARGET_DIR=~/.cache/nornir-bench-target cargo build --release -p gunnar-cli --features http --bin gunnar`, then</text>
<text x="56" y="1130.5" fill="#bcd9ec" font-size="9.5" text-anchor="start">`NORNIR_BENCH_ONLY=gunnar.vs_forge_ nornir bench run gunnar --heavy --config release/docs-nornir.toml`.</text>
</svg>