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

nordisk/nornir

.nornir/bench-live-pane.md

📡 Bench LIVE pane — surface a bench as it runs (P1.3 / C6d)

Design + guide for the LIVE telemetry panel in the nornir viz Bench tab (src/viz/bench_live.rs, rendered first inside Tab::Bench by src/viz/ops_tabs.rs::BenchState). P1.3 turns the bencher's CPU/mem telemetry (P1.2's bench_telemetry table) into a live surface: as a bench runs the pane names the running bench, shows its green/red status, a cores-busy / N indicator, and a small CPU sparkline — updating as rows land in the warehouse. THE_BIG_PLAN LAWs apply: everything rendered is exposed as data (state_json()["bench"]["live"] + $NORNIR_VIZ_TRACE bench.live), the test inject-and-asserts real values, and a wgpu PNG snapshot is the acceptance artifact.

Why

P1.2 built CPU/mem telemetry into the bencher: a 1 Hz /proc/stat sampler folds per-bench min/avg/max cores-busy (of N logical cores) + peak mem and streams them — [cores-busy C/N] on each [BENCH] line, and a dedicated warehouse bench_telemetry table (run_id, repo, bench, n_cores, cpu_pct_*, cores_busy_avg/max, mem_peak_mb, mem_pct_max, elapsed_ms). But the viz Bench tab only charted historical metric series — the live footprint never showed. P1.3 adds the panel that lights up as the bench runs.

What it shows

A list over the warehouse bench_telemetry table (one row per (run_id, repo, bench)), newest run first, each bench unit as one row:

element meaning
status dot green ✓ / red ✗ / neutral ·, derived from the run's paired tests outcome (see Status)
bench NAME the bench unit id (znippy.compress, holger.ops, …)
cores / N proc_cores_max / n_cores — this process's peak occupancy (task-clock ÷ wall), colour-ramped (green few → amber mid → red pegged). NOT cores_busy_max: that is /proc/stat, the whole box, and reads N/N for a strictly serial arm on any loaded machine (measured on oden 2026-08-03 — gunnar.walk_1m at 32/32 while holding 0.56 of one core). A row from before 2026-08-03 has no such measurement and must render as ?, not 0.
host-busy / N cores_busy_max / n_cores — ambient context, explicitly labelled as the host so it is never mistaken for the bench's parallelism
CPU sparkline a tiny 3-point trace: [cpu_pct_avg, cpu_pct_max, peak-busy-fraction] (0..100) — HOST-WIDE, so it is the shape of how hard the box was pushed
cpu % cpu_pct_max as a numeral (host-wide)
mem badge mem_peak_mb when > 0
header strip the live run_id + green/red/neutral tallies + a ↻ re-read button

Status — green/red per bench

The bench_telemetry row has no pass/fail; the verdict comes from bench_runs. bench_status(repo, bench, runs) pairs a bench with a TestOutcome whose name matches the bench exactly or against the bench's last .-segment (znippy.compress ↔ test compress):

Live update — as rows arrive

Source of truth = the warehouse (LAW persist-to-warehouse-stream-not-tmp). Local mode opens the warehouse read-only (lock-tolerant, like the 🧪 Test / 🚀 Release tabs) and reads bench_telemetry + bench_runs. While the tab is open, draw() re-reads every RELOAD_EVERY (~1.5 s) and calls request_repaint_after, so telemetry rows that land mid-run (P1.2 appends per run) appear without user input. A ↻ button forces an immediate re-read; a workspace switch / reload (app.reload() → BenchState::reload()) drops the cache so the next draw re-hydrates.

Remote (thin-client) mode shows a note — there is no Bench.Telemetry RPC yet (same split as Test / Release / Leaderboard). The existing remote Bench history charts are unchanged and render below the live panel.

Architecture — pure fold + thin renderer

bench_telemetry rows ─┐
                      ├─ fold_live() ─► Vec<LiveBench> ─► draw_live_row() (egui)
bench_runs (tests) ───┘                      │
                                             └─► state_json()["bench"]["live"]
                                                 $NORNIR_VIZ_TRACE bench.live IN/OUT/END

fold_live() is pure (telemetry + runs → Vec<LiveBench>) and unit-tested in isolation. LiveBench::cpu_spark() / cores_label() are the exact values the cell paints, surfaced verbatim in state_json so the headless matrix asserts the rendered content (LAW #6 — "see what the user sees" as data, not pixels).

Ordering: newest run first (run_id descending as a time-prefixed proxy), then by bench name within a run.

state_json shape (LAW #6)

state_json()["bench"]["live"]:

{
  "source": "local /…/warehouse",
  "error": null,
  "live_run_id": "run-2",
  "count": 3,
  "green": 2, "red": 1, "neutral": 0,
  "benches": [
    {
      "run_id": "run-2", "repo": "znippy", "bench": "znippy.compress",
      "status": "green", "ok": true,
      "n_cores": 8, "cores_busy_avg": 6.5, "cores_busy_max": 7,
      "cores_label": "cores-busy 6.5/8 (peak 7/8)",
      "cpu_pct_avg": 55.2, "cpu_pct_max": 92.0,
      "mem_peak_mb": 512.0, "elapsed_ms": 2200.0,
      "cpu_spark": [55.2, 92.0, 87.5]
    }
  ]
}

bench.live IN/OUT/END trace events carry the same benches payload + the green/red/neutral tally.

Test (inject-and-assert) — tests/viz_bench_pane.rs

bench_pane_exposes_live_telemetry builds the app, injects concrete bench_telemetry rows + paired bench_runs via UrdrThreadsApp::inject_bench_telemetry_for_test, selects the Bench tab, and asserts state_json()["bench"]["live"]:

viz_snapshot_bench_pane renders the pane via egui_kittest wgpu, asserts the populated state (count=3, red>=1), and writes tests/snapshots/viz_bench_pane.png (LAW #4). Unit tests in bench_live.rs cover fold_live pairing/ordering and the state_json round-trip.

Files