Files
vtroc 2f71d93b3a Package what each run measured, and decode the match's audio once.
`make bench` printed two reports and left nothing behind but a text file, so a
number could be compared with another number from the same afternoon and not
much else. It now prints three and writes the run's own directory: `report.txt`,
`manifest.json` and `summary.json` beside the `trace.pftrace`.

Section [3/3] is the counters — the render stages, the GPU passes, and the
per-frame work census. A counter nobody sampled prints MISS rather than
`0.00`, and the counters and spans this build declares but this run never
reached are named at the end, so a hole in the instrumentation cannot read as a
fact about the workload. GPU passes overlap and resolve late, so they are never
summed.

`manifest.json` says what produced the number: the revision, the dirty flag,
the toolchain, the profile cargo actually built under, the locked `bevy` and
`wgpu`, the adapter and whether its device can time a pass, the window, the
pools, the env toggles and the cache state. The build half is baked in at
compile time, because asking `git` at runtime names the tree the process is
standing in rather than the build. A fact nobody could read is `null`, never a
plausible default.

Reading that report, `preparing match audio` was the largest stage of every
load — one row holding one number, which cannot tell a thousand cheap byte
loops from five hundred external processes. It is the second. Section [1/3] now
splits the stage by the decoder each clip took, in worker time, and counts what
each one prepared, failed, held and left resident.

The T5 XWMA decode goes through the artifact cache under `xwma_pcm`, keyed on
every input the decode reads, so a clip is decoded once per machine rather than
once per launch. A cold load still had to pay the first time: 515 `ffmpeg`
processes, 18.9 s of worker time, almost none of it decoding. A worker now
takes the jobs already queued and answers up to 64 clips through one process —
every clip its own input and its own output, nothing concatenated — which is
1.3 s for the same bytes. Failure stays per clip: a batch `ffmpeg` refuses is
re-asked one clip at a time, leaving the bad clip as the only failure.

`make bench-overhead` can point at either recorder, `VAR=IW4L_PERF` or
`VAR=IW4L_BENCH`; they are separate costs and each needs its own pairs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 19:43:47 +03:00

2.7 KiB

Native Perfetto

Runtime observation is iw4l-artifacts/runs/<uuid>/trace.pftrace. Recording is off unless IW4L_PERF=1 (make bench / make bench-live / make scenario / make chaos / make lifecycle-* force it on). Where the run spent its time, rather than one frame, is BENCH.md.

make bench-live                            # record + compact p50/p90/p95/p99/hot-path report
make bench-overhead                        # 10 alternating on/off pairs + paired 95% CI
make bench-overhead VAR=IW4L_BENCH         # the same, for the in-process bench recorder
cargo xtask perf-overhead --pairs 10 --bin target/play/iw4l --zone mp_highrise --cmds 'wait world; wait 8s; quit' --focus script_model:192
make scenario                              # scripted match: IW4L_PERF=1, then cargo xtask scenario
make chaos                                 # RPG splash run: IW4L_PERF=1, then cargo xtask chaos
make lifecycle-swap                        # occupancy: IW4L_PERF=1, then cargo xtask live swap
make lifecycle-all                         # run all five occupancy recipes and read them back
cargo xtask bench                          # rerun the report on the newest run
cargo xtask bench iw4l-artifacts/runs/<uuid>
cargo xtask scenario                       # scripted-match report on newest .pftrace
cargo xtask chaos                          # RPG-splash report on newest .pftrace
cargo xtask net-feel                       # input-to-photon report on newest .pftrace
cargo xtask live swap                      # lifecycle occupancy on newest .pftrace
cargo xtask query slices.sql
cargo xtask query <uuid> render.sql         # render topic pack
IW4L_PERF_FOCUS=script_model:192 make map mp_highrise CMDS='wait world; wait 5s; quit'
cargo xtask query <uuid> render_owner.sql   # selected owner plan → DrawIndexed
cargo xtask query "SELECT name, COUNT(*) FROM slice GROUP BY name"

Each run has trace.pftrace + manifest.json. Optional reports are offline from Trace Processor (xtask/perfetto/*.sql). The runner fetches https://get.perfetto.dev/trace_processor if TRACE_PROCESSOR / PATH are empty. The in-process ring is 32 MiB. Every reader rejects Perfetto errors, drops, and overwrites; buffer exhaustion fails the read rather than reporting a partial trace.

Pin the run UUID and verify the manifest printed by every reader: zone, role, workload, and git must match the claim. The implicit newest run is a convenience, not a citation. A state-only question may use console dump [name], which writes one iw4l-artifacts/dumps/*.txt; it contains no history or timing.

Per-item overlay/draw spans are forbidden. IW4L_PERF_FOCUS admits one owner; without it there is no owner census. Semantic events stay rare (player_tick, death, feel, …).