c132243b3022415e68a5fef9e3969c0b46e3c3af
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c132243b30 |
feat(worms): asset-owned worm off switch; offline stack symboliser -- cheap-worm default to be reverted
Worms round (Codex; reviewed by me against its report and PERF-SAMPLED.md).
Part 1, a real off switch: UVoxelStrateDefinition::bEnableWorms (default true, so existing assets
are unchanged). It is deliberately not in FStrateGenerationParams, so the 83-field memo-key guard
is untouched. It resolves after composer candidates, season vectors and boundary blending, is
included in the generation fingerprint, and clamps composer overrides, so a roll cannot re-enable
it. With strength 0 the fused path skips the worm block, the op-stack builder omits the worm
source, and the interval/classifier paths do no worm work. Proof: disabled == hand-zeroed control
byte for byte (70D310A4, 74,672 triangles), capability 20,856 / 10,917, connected, zero worm rows
in the symbolised sampler. voxel.WormsForceOff is a dev-only, world-changing measurement
override, included in the fingerprint.
Part 0: the sampler writes a module map (733 modules, identity checked), and a post-exit
offline symboliser (VoxelStackOfflineSymbolizer, editor module) rebuilds the all-LOD / LOD0 /
LOD1+ exclusive / inclusive / source-line tables. A planted cost was found (5 named rows) and then
removed. DbgHelp still never runs in the measured process.
Part 2, "cheap worms": FAILED to deliver, and the default is wrong. No candidate was faster:
lattice-2/4 gave no gain, and value noise was within noise static and ~2.1% WORSE in moving worker
seconds. Yet this commit carries voxel.WormNoiseMode=1 (value noise) as the default, a field change
(29/64 test tiles) with no benefit, selected after value-noise variants were tried until one
passed capability (three were rejected at 20,821-20,828). The next round reverts the default to
exact Perlin (mode 0) and removes the no-gain world-changing experiment modes.
The finding that matters: disabling worms entirely (WormsForceOff A/B, two interleaved runs each)
saves only ~2.5% static and ~3.4% moving worker time. The sampler's "worm line
VoxelGenerator.cpp:3985 = 19.4% of LOD0" was a misattribution of inlined noise code to one PDB
line. Line-level attribution of inlined code must be confirmed by ablation before a round targets
it. That is why the worm block skip (
|
||
|
|
26b34765d5 |
perf(generation): exact worm block skip with a proven 3D Perlin bound -- measured NEUTRAL
The sampler put the worm carve's first noise call (VoxelGenerator.cpp:3985, FusedWorm) at 19.4% of LOD0. The worm only carves where N1 + N2 < T with N2 >= 0, so where N1 >= T it contributes nothing, bit for bit. A block proven to have N1 >= T everywhere can skip both noise calls exactly. - The Lipschitz bound for this Perlin3D implementation: L = (15/4) * sqrt(3) * 1.25 = 8.118988160, in scaled-N1 units, with a 1e-3 float-rounding margin. Adversarial sampling found a worst ratio of 3.187 (0.39 L). The existing 2D floor-relief bound (8.5) is a component bound of a different field and was confirmed not to transfer. - A soundness test that can fail (VoxelForgeWormBlockSkipTest): 6,476 proved blocks, 355,945 exact lattice samples, 0 violations. A deliberately halved L proves a bad block; the full L rejects it. - voxel.WormBlockSkip, default 1. Field exact: export OBJ byte-identical (b3e5f4c3..9b377), 841/841 keyed tiles identical to NextHeadStaticTrace_20260913, capability 20,830 / 10,909. RESULT, stated plainly: no measurable speed gain. The skip rate is 4.07% of worm-eligible LOD0 samples and 0% at LOD1-4, because the bound is ~2.5x looser than the worst observed ratio at the block size used. Paired on-vs-off, two interleaved runs each: static worker +2.15%, generation p50 +1.8%; moving worker -1.42%, request p50 -2.5%. Within noise. The next round turns the default OFF. The proven bound and its test are kept for a tighter skip later (smaller or hierarchical blocks, or a per-cell bound). Run ledger (from Codex's report): the first build failed on an ambiguous test overload and was fixed; two timing runs are void because the orchestrator stopped the session so the owner could use the PC; the eight valid timing runs, the sampler run and the diagnostics runs completed. No new crash folders. PERF-SAMPLED.md now lists all eight earlier-round UECC crash records. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y |
||
|
|
ab4bbe3680 |
perf(generation): field-exact LOD0 overhead removal; the unknown bucket is Core.dll
Field-preserving floor round (Codex; reviewed by me against the code):
- exact per-(sample, passage) caching of native/generic floor projections and walkable-air
checks, behind voxel.FloorRound1PassageProjectionCache
- a zero-work disabled path for VoxelDensityProfile::FScopedTimer, unchanged when enabled
- pointer / range-for iteration in the proven-hot tunnel, passage, spatial and candidate loops
(TArray RangeCheck was ~6.5% of LOD0 samples on the Development binaries the owner plays on)
- FVector2D temporaries replaced with scalar doubles, same operations in the same order
- FSurfaceColumn made trivial, so thread-local construction does no work. Verified safe:
ComputeSurfaceColumn writes all five outputs unconditionally before any branch, and every read
of Box.Cols is gated by Box.Computed
Verified: export OBJ byte-identical (b3e5f4c3..9b377); capability 20,830 / 10,909; all 8
interleaved game runs gave identical totals (841 tiles / 669,834 triangles static, 2,348 /
1,985,216 moving, the same as previous rounds). Per-tile identity for the always-on changes still
needs a check against a stored trace; that is queued first in the next round.
Game path (sampler off, two interleaved runs each):
projection-cache switch alone: neutral (static worker +0.41%, moving -0.05%)
vs the previous round (
|
||
|
|
8e40bdd92c |
measure(perf): in-process stack sampler; the game spends ~3/4 of worker CPU on room player-fit
A diagnostic stack-sampling profiler, off by default. A sampler thread captures the stacks of
registered generation workers with FPlatformStackWalk::CaptureThreadStackBackTrace every N us and
tags each sample with the tile's LOD. Symbolisation (inline frames included) happens after the
run, into a bounded CSV plus a summary. Registration is per tile task, never per sample; when the
sampler is off it costs one relaxed atomic load. Game: -voxel.SampleStacks=<us>. Commandlet:
-samplestacks[=us]. The streaming latency line now also prints applied tile, visible tile and
triangle counts.
Instrument checks (Codex, PERF-SAMPLED.md):
- a planted 20 ms busy loop was found at 10.5% inclusive, the expected share, then removed
- closure: 193k static / 251k moving samples, 0 capture failures, <0.75% wait frames,
leaf resolved for 82-84%
- static game output identical with the sampler on or off: 841 tiles, 447 visible, 669,834
triangles
- overhead at 1 ms: +4.7% static worker CPU; moving worker CPU within noise, moving timing +4-5%.
So trust large shares, not single-digit deltas.
The finding, on the game path (the owner's instruction: profile the game, not the export):
all-LOD inclusive static moving
BuildChunkCache 79.5% 80.5%
VF_FindPlayerFitPointForRoom 73.4% 74.0%
About three quarters of in-game worker CPU goes to player-fit searches for rooms. They are
recomputed on every per-worker room-cache rebuild, and coarse tiles span many cache windows. At
LOD0 the costs are the cave warp (3 Perlin per sample), the swept tunnel evaluated both in the
SDF and in EvaluateTunnelCoreWorld (~12% inclusive each, duplication confirmed), and the passage
structural tail (~11-14%). Candidate AABB rejection is cheap (1.5-2.2%). The 74-field params copy
does not show up as a separate cost.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZS94UH8bSsBeg5sZaxe2Y
|