Files
benilla-octo/measure.sh
T
gromandClaude Opus 5 e4851df7df ui: WOW_UI_PHASES — the UI pass's phase split, windowed
`WOW_UI_TICK` measures `UiScript::tick`. `[sys-timing]` measures the Bevy system around it,
`ui_script::extract::tick_script`, which also runs the FontString measure round-trip and
`UiScript::resolve`. In a combat session the first read 38 ms/s — its lowest of the whole run —
while the second read 157 ms/s. Neither instrument was wrong, and 76% of the cost was in the space
between them, which is where the stutter players report lives.

`UiFrameCost` has carried the whole split since the hover recorder needed it, and `WOW_UI_COST=1`
prints it one line per frame: at this client's framerate, twenty thousand synchronous stderr writes
a minute from the thread being measured, which is the trap `sys_timing`'s header records costing
15 ms a frame. The split existed and was unusable in exactly the situation it was for.

So a window, and a worst column — a phase that is ruinous on one frame in a hundred has a mean near
nothing and is the only thing a player feels. `derives` is in the per-frame line because
`UiFrameCost::derives` is documented "expected zero", so a nonzero one names a whole-graph
re-derivation on sight.

`UiFrameCost::stamped` comes with it: the paint can return before any of its three arms wrote this
frame's phases, and a reader that cannot tell counts the previous frame twice.

Armed in ./measure.sh at 5 s, with the per-frame form left alone for the hover recorder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
2026-10-20 18:17:01 -06:00

48 lines
2.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Launch benilla with the measurement instruments armed, so a play-test answers "where did the
# frame go?" instead of only "it felt slow".
#
# ./measure.sh # the standard set: per-system ms, the UI tick split, per handler
# ./measure.sh --stalls # the above plus the per-binding bill, which is noisier and ~2% of a
# # frame; only worth it when a handler's own time is already named
#
# Everything ./run.sh does, it does: it builds, it logs to logs/latest.log and logs/errors.log.
# The instruments print to stderr, which run.sh already captures, so the numbers land in the log
# beside the errors.
#
# Why a script and not a line of environment variables: the set is long, and a run with one of
# them missing looks exactly like a run that found nothing. This prints what it armed.
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Per-system milliseconds once a second, and the stall watchdog that names a system still running
# after WOW_STALL_MS. This is the one that says whether a frame went to the UI or the world.
export WOW_SYSTEM_TIMING=1
# The UI *pass*, split into its phases: tick, the FontString measure round-trip and layout
# resolve, extract, convert, the mesh rebuild. The one that distinguishes "the addons are slow"
# from "the layout is resolving a dirty graph", which the tick split below cannot see — the first
# session with this armed found 11.7 ms a frame outside the tick in a run whose handler profile was
# clean.
export WOW_UI_PHASES=5
# The UI tick, split into its eleven engine phases, with `C_Timer` fires credited to the line that
# armed them. Inside WOW_UI_PHASES' `tick` row.
export WOW_UI_TICK=5
# Per handler: self and total time, calls, and the worst single call — the column that names a
# handler stalling one frame in five hundred, which no mean can show.
export WOW_UI_HANDLERS=5
# The frame's own cost, split main thread against GPU, so a dip says which side it is on.
export WOW_FLASH_TRACE=1
export WOW_GPU_MS=1
if [ "${1:-}" = "--stalls" ]; then
# Per Lua binding. It wraps the bindings, not the globals — wrapping the globals is what
# unloaded a third of the interface once (`setfenv` is level-relative, see
# `benilla-ui/src/script/verb_prof.rs`).
export WOW_UI_VERBS=5
shift
fi
echo "measure: armed —"
env | grep -E '^WOW_(SYSTEM_TIMING|UI_PHASES|UI_TICK|UI_HANDLERS|UI_VERBS|FLASH_TRACE|GPU_MS)=' | sort | sed 's/^/ /'
exec "$root/run.sh" "$@"