Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

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>