Whole-tree sync: cad-core/cad-ui split sources, nigig-build construction_frame migration, pdf port progress, mpesa/pay/uikit/doc updates, workspace members/profiles/lock, CI workflows and reviews. See individual file history for details.
3.4 KiB
nigig-traffic performance runbook (plan Phase 6)
Rule zero (HPC ch.1 "When to Optimize", ch.5): no optimization without a
profile line pointing at it, and no number counts until it is in
PERF_BASELINE.md. If it isn't in the baseline file, it didn't happen.
Live frame stats
The view collects tick + draw microseconds over a 300-sample window and
reports p50/p95/p99 to the log (src/ui/game_view.rs: FrameStats).
Collection is a few integer stores per frame; reporting only fires with:
NIGIG_TRAFFIC_STATS=1 cargo run -p nigig-traffic
# look for: traffic stats: tick us p50=.. p95=.. p99=.. (n=300)
Repeatable harness
cargo test -p nigig-traffic --test perf -- --test-threads=1 --nocapture
tests/perf.rs prints median build µs per category and per-tick µs over
600 ticks × every scenario. Timings are deliberately NOT asserted —
wall-clock asserts are flaky by nature (HPC ch.5.6). The sanity asserts
(tick count, Intro→Driving transition, black-boxed score) exist only so
the optimizer cannot hollow the loop.
Methodology (HPC ch.5.6, condensed)
--test-threads=1, warmed runs, one machine, nothing else building. (Backgroundcargo-makepad androidjobs invalidated early readings here — checkps aux | grep rustcbefore trusting a number.)- Report medians, never means; re-run to check outlier stability. Outliers that move between runs are noise, not findings.
- Debug profile numbers are for shape (which function, which scenario class), never for absolute budgets. Ship budgets come from release runs on the target device.
Statistical profiling
- macOS: Instruments → Time Profiler, attach to
target/debug/nigig-trafficwhile driving; look forstep_world,draw_scene,evaluate, text shaping (shaper.rs), not the frame loop itself. - Linux:
perf record -g+perf report. Top-down first, bottom-up to confirm callers. - Sampling finds the hot 3%; instrumentation lies about the rest. The
FrameStatsring is the instrumentation — treat it as a tripwire, and reach for the sampler before changing code.
What the numbers said so far
See PERF_BASELINE.md. Headline: the tick is memory-latency-bound on
tiny data (median ~15–25µs debug, 47 scenarios), builds are single-digit
µs. There is no compute hot spot to SIMD away — Phases 7–8 accordingly
target lookups, allocations, and branches, not intrinsics.
Build environment notes (hard-won, do not re-learn)
RUSTC_WRAPPER=kacheis exported on dev machines (~/.profile) but thekachebinary may not exist — cargo then fails withcould not execute process kache ... os error 2, surfacing in makepad-test runs asBuildStopped 101. Fix:ln -s sccache ~/.cargo/bin/kache, or standardize onsccache+SCCACHE_DIRin one place.- One target dir: the workspace root
target/. Per-cratetarget/dirs (a stale 2.2G one lived undernigig-traffic/until Sep 2026) waste disk and split test artifacts across two trees. The makepad-test harness historically pointedCARGO_TARGET_DIRat the per-crate dir; keep it on the workspace root. - Release-vs-debug profile decision (
opt-level 3vszfor the game binary) is BLOCKED on a green workspace build — the fork rev bump broke compilation repo-wide (see Phase 9 notes in PERF_BASELINE.md). Measure both on the Phase-6 bench before choosing; do not pick on aesthetics.