- Per-frame passage-overlap scoring over outstanding tiles (added in L85) measured 121 ms for 407 LOD2 candidates; removed (deterministic desired/travel order kept). - Visibility reconciliation read-locked the Core world per candidate while the far-world build held the writer lock (162 ms scan, 123 ms lock wait); the immutable render distance is cached. Visibility now <= 0.17 ms. - Ready player-support collision tiles stay in the collision-desired set: fixes the L95 SW outlier (LOD0 (3,0,-47/-48/-50) re-admitted); 6 SW runs without re-admission. - AVoxelWorld::Tick during the arrival burst: FI p99 3.7-4.3 ms (max 6.0), SW p99 8.2-8.9 ms (max 11.2). FI still shows rare 40-60 ms world ticks outside our actor scope (engine tick group). - Near-set times kept: FI 2.06-2.12 s, SW median 1.30 s after first admission. World unchanged. Fast tier PASS; Linux UE host builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
41 lines
4.8 KiB
Markdown
41 lines
4.8 KiB
Markdown
# Linux L96: WorldAssembler frame spikes
|
||
|
||
## What the timings show
|
||
|
||
The starting point is L95 after its queue fix. `frame_ms` is the interval between apply-frame entries, not a rendered VR frame; this commandlet manually ticks the world and sleeps about 16 ms each loop.
|
||
|
||
| Arrival | L95 p95 / max interval | L96 p95 / max interval |
|
||
|---|---:|---:|
|
||
| FI | 20.8–21.3 / 118–122 ms | 19.25–20.12 / 22.4–202.8 ms (5 runs) |
|
||
| SW | 24.7–27.6 / 118–145 ms | 23.27–25.12 / 25.08–30.98 ms (6 runs) |
|
||
|
||
I used a target of p99 ≤16 ms and max ≤25 ms for our `AVoxelWorld::Tick` work. That scope met it:
|
||
|
||
| Arrival | `AVoxelWorld::Tick` p99 | Maximum |
|
||
|---|---:|---:|
|
||
| FI | 3.71–4.26 ms | 4.12–6.04 ms |
|
||
| SW | 8.24–8.89 ms | 8.39–11.19 ms |
|
||
|
||
The residual FI interval spike is outside that actor scope. A repeat measured `AVoxelWorld::Tick` at 2.17 ms and the enclosing `UWorld::Tick` at 39.50 ms; 39.46 ms fell between Unreal’s pre-actor and post-actor tick delegates. Another FI repeat measured a 62.23 ms full world tick, with a 16.16 ms commandlet sleep in the same loop. The earlier 202.8 ms interval had a 185.6 ms aggregate `TestWorld->Tick` maximum. These are measured world/tick-group delays; the specific engine child timer was not isolated. A task-group wait or scheduling stall is inference, not proven. SW’s full-world-tick p99 was 8.78–11.79 ms, max 14.82 ms.
|
||
|
||
GC callbacks recorded zero collections during all measured bursts. RMC section-group creation peaked at 2.37 ms; collision handoff at 0.022 ms and completion handling at 3.96 ms. Per-frame JSON writing peaked at 1.46 ms; final trace flush peaked at 4.99 ms during teardown. The commandlet readiness check stayed below 0.01 ms, while its deliberate sleep was normally 16.2 ms. These commandlet timings cannot explain the 39–62 ms world tick by themselves.
|
||
|
||
The headless `-trace=cpu,frame` capture produced `.utrace` files. Unreal Insights export could not start: SDL failed to initialize UDEV in this headless environment. Custom per-frame timings identify the actor/tick-group interval, but not its engine child scope.
|
||
|
||
## Fixes and throughput
|
||
|
||
- Removed the per-frame passage-overlap scoring pass over outstanding tiles. It measured 121.37 ms for 407 missing LOD2 candidates; the remaining candidate scan, sort, and admission scopes were each below 0.32 ms. Existing deterministic desired/travel ordering remains.
|
||
- Cached the immutable Core render distance instead of read-locking the Core world for every visibility candidate. Before the fix, one visibility scan took 162.03 ms and a measured `WorldLock` read wait took 122.65 ms while the far-world build held the writer lock. Afterward, visibility reconciliation peaked at 0.17 ms in FI and 0.14 ms in SW. The writer contention is inferred from the matching build path and lock timing.
|
||
- Kept ready player-support collision tiles in the next collision-desired set, preventing an unload/re-admit cycle for support keys.
|
||
- Kept the L95 queue settings. FI settled in 2.061–2.124 s (L95: 2.068–2.114 s). Six SW runs had a 1.300 s median (range 1.282–1.544 s); four were 1.282–1.328 s. A seven-worker experiment slowed SW and did not reliably remove the outer world-tick stall, so it was removed.
|
||
|
||
The L95 SW outlier keys were LOD0 `(3,0,-47)`, `(3,0,-48)`, and `(3,0,-50)`. They were non-empty, collision-ready floor/support tiles that fell out of the rebuilt collision-desired set, then were unloaded and requested again. This is the same support-retention family as L92’s empty-collision churn, but these were not empty results. Traces showed no cancellation or epoch race. The retention fix kept each key to one request in SW repeats 27–32; all six had `repeated_lod0_request_keys_before_settle=[]`. One run reached backlog 256 and settled in 1.544 s without re-admission.
|
||
|
||
Fast tier passed: 274 checks, 80 runs, 300.6 s. Final Linux UE build passed with `-NoUBA -MaxParallelActions=4`. Timed processes were pinned to CPUs 0–11; Unreal used no `--as`. Headless fast tier used `prlimit --as=6442450944`.
|
||
|
||
## Source and failed diagnostics
|
||
|
||
Changed: `Source/VoxelForge/Private/VoxelWorld.cpp`, `Source/VoxelForge/Public/VoxelWorld.h`, `Source/VoxelForge/Private/VoxelStartupTrace.cpp`, `Source/VoxelForge/Public/VoxelStartupTrace.h`, `Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp`, and `Standalone/Research/WorldAssemblerM3/Linux96/RunL96UnrealArrival.py`. No passage-composer source changed.
|
||
|
||
The first fast-tier launch lacked its binary; it was built and the tier then passed. One diagnostic mistakenly applied the headless 6 GiB `prlimit` to Unreal and failed module loading; subsequent Unreal runs had no address-space limit. Insights export attempts failed on SDL/UDEV; no installs were made. An initial build could not write UBT’s default log under read-only `~/.config`; builds with explicit `-Log` passed. No run exceeded 30 minutes. No commit, push, or stash was made.
|