acdream/docs/research/2026-07-05-mp0-baseline.md
Erik 86d7094451 docs(pipeline): MP0 baseline captured - gate PASSED, alloc-triage amendment
Full-route capture (Holtburg -> dungeon 0x0007 -> town 0xCE94 -> dungeon
-> Holtburg, 4 teleports) plus the supplemental town run. Verdict: the
render-side-CPU split is confirmed (GPU <= ~2.7ms, upd/upl/imgui ~= 0),
steady medians beat the spec assumption (worst town p50 3.6ms), and the
smoothness gap is GC: 1.5-3 MB allocated per frame drives gen2
collections 1-2/s and every town p99/max violation, while dungeon
windows (zero gen1/gen2) are spike-free at ~2000 fps. Teleport hitch
quantified at 211ms worst frame / 75.7MB single-frame allocation.

Gate decision: PROCEED to MP1 unchanged, with one recorded amendment -
a bounded post-MP1 allocation-triage session for churn sites outside
the MP3 rewrite surface. Fort Tethana axiom view still to be
re-measured at the MP2/MP3 gates.

MP0 complete: profiler shipped (7d74c68c..4b44a152) + baseline + gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 19:42:36 +02:00

5.7 KiB
Raw Blame History

MP0 baseline — frame-profiler capture, attribution verdict, gate decision

Date: 2026-07-05. Build: Release, ACDREAM_FRAME_PROF=1, vsync off. Character: +Horan (0x5000000B, testaccount2). Logs: mp0-baseline.log (primary run: full route with 4 teleports, ~2 min) + mp0-baseline-run3.log (supplemental: longer town session, same build) — both untracked in the worktree root; the numbers below are transcribed from them.

Route (reconstructed from log markers): login → Holtburg outdoor (0xA9B4) → movement/turning → teleport 1 → dungeon 0x0007 (dungeon collapse streaming path) → walk → teleport 2 → outdoor town 0xCE94 → pan/walk → teleport 3 → dungeon again → walk → teleport 4 → Holtburg → close (graceful).

The numbers (per 5-second [frame-prof] window)

State cpu_ms p50 p95 p99 max gpu_ms p50 alloc/frame p50 GC gen0/gen1/gen2 per 5 s
Login settle (Holtburg) 2.1 10.2 52.8 123.3 0.4 1.57 MB 93 / 50 / 36
Holtburg steady/turning 1.62.0 2.22.6 2.83.7 1520 0.4 1.581.60 MB ~80 / 313 / 35
Dungeon 0x0007 walk 0.40.5 0.50.6 0.9 1.21.5 0.1 0.500.63 MB 119 / 0 / 0
Town 0xCE94 pan/walk 2.93.6 4.05.0 7.215.7 2637 1.21.6 2.53.0 MB ~60 / 522 / 511
Teleport→dungeon window 2.8 3.3 14.1 211.2 1.3 (max 75.7 MB in one frame) 62 / 11 / 10
Teleport→outdoor windows 1.22.9 3.43.8 4.57.0 33 0.21.5 (max 2031 MB single frame) ~8093 / 1526 / 5
Holtburg end-of-run moving 2.62.9 3.35.1 8.523.0 2087 2.52.7 2.14 MB ~5058 / 2432 / 78
(run 3) town views 1.72.4 2.73.4 7.915.8 2939 0.42.2 1.72.2 MB ~70109 / 1732 / 611

Stage attribution (upd / upl / imgui): ≈ 0.0 ms p50 in every window (occasional upd p95 0.11.7 during streaming). GPU never exceeded p95 3.7 ms.

Attribution verdict

  1. Steady-state medians are far better than the spec §1 assumption. The spec assumed ~6 ms / ~165 FPS dense-town CPU. Captured: worst steady town view is p50 3.33.6 ms (~280300 FPS); Holtburg ~1.62.4 ms (~400600 FPS); dungeons 0.40.5 ms (~2000 FPS). Caveat: the canonical Fort Tethana/Arwic "dense worst-case axiom view" was NOT in this route — the §1 numbers came from there. The 300-FPS throughput goal is close to met in the captured views but must be re-verified at that view before any "target met" claim.
  2. The frame is CPU-render-side, as assumed. GPU ≤ ~2.7 ms everywhere; upd/upl/imgui ≈ 0 → virtually all CPU time is unattributed render-side submission. The spec's MP3 thesis stands.
  3. The smoothness gap is real, and it is GC. Every town window violates the ≤16 ms goal at max (2087 ms) and several at p99 (up to 23 ms), while dungeon windows — the ONLY windows with zero gen1/gen2 collections — are spike-free (max 1.5 ms). Steady-state allocation is 1.53.0 MB per frame (≈ 0.61.2 GB/s at these frame rates), driving gen2 collections roughly 12 per second in towns. The correlation is airtight across all 28 windows: spikes appear exactly where gen1/gen2 counts are non-zero.
  4. The hitch scenario is confirmed and quantified. Teleport-into-dungeon produced the worst frame of the session (211 ms) with a 75.7 MB single-frame allocation (decode + hydrate storm); login settle hit 123 ms max with 36 gen2 collections in one window. This is exactly the class MP1 (bake — load becomes mmap reads, not managed decode) targets.

Gate decision (spec §5)

PROCEED to MP1, with one amendment. The assumed cost split (render-side CPU dominant, GPU idle, hitches at load boundaries) is CONFIRMED, so the phase order stands. The amendment: the capture elevates steady-state allocation churn from "MP4 cleanup" to a first-class smoothness lever — 1.53 MB/frame of per-frame garbage is the direct cause of the town p99/max violations, and it is NOT load-related (it persists in steady windows).

Amendment (recorded in the spec): after MP1 ships, run a bounded steady-state allocation triage (one session: dotnet-trace / allocation-sampling on the live client, identify the top ~5 per-frame churn sites). Sites inside the MP3 rewrite surface (per-frame submission rebuild — the prime suspect for MB-scale per-frame lists) are left for MP3 to remove structurally; sites outside it get targeted fixes immediately. The full zero-alloc pass remains MP4. This front-loads the biggest smoothness win without letting "fix allocations" sprawl into an unphased rewrite.

Follow-ups noted (not gates)

  • Re-measure the Fort Tethana axiom view at the MP2 and MP3 gates (the degrade port materially changes exactly that view).
  • upd ≈ 0.0 everywhere is suspicious-adjacent but plausible (light sim in these scenes); if a future capture shows heavy OnUpdate work still reading 0.0, verify the stage scope wasn't bypassed by an early return upstream.
  • Dungeon at ~2000 FPS with zero GC pressure is the existence proof that the engine's frame loop CAN run allocation-free at four-digit FPS — towns are paying for churn, not for fundamentals.

Apparatus state

[frame-prof] (env ACDREAM_FRAME_PROF=1, DebugPanel "Frame profiler" checkbox) is PERMANENT — it is the measurement instrument for every MP gate. Implementation: src/AcDream.App/Diagnostics/ (FrameStatsBuffer / GpuFrameTimer / FrameProfiler), plan docs/superpowers/plans/2026-07-05-mp0-frame-profiler.md, commits 7d74c68c..4b44a152.