Move closed audits, task briefs, design rounds, perf reports, the OPSTACK plan/progress logs and WORK-NEXT.md into Docs/archive/ (git mv, history kept). Whitelist Docs/ in the strict .gitignore, ignore Tools/**/__pycache__/, and remove the tracked .pyc. Repoint references in the kept docs and in non-test source comments to the new paths.
141 lines
7.6 KiB
Markdown
141 lines
7.6 KiB
Markdown
# Task 2 performance report
|
|
|
|
Date: 2026-09-09
|
|
Repository: `E:\Projet Unreal\VoxelM\Plugins\VoxelForge`
|
|
Work began at `experimental` / `e63f016`.
|
|
|
|
## Measurement rule
|
|
|
|
All performance numbers below are per-case `run.elapsed_seconds` from the batch report. This is
|
|
the wall clock for a real commandlet case through the export or render result; it includes the
|
|
shared-density-grid build when enabled. `summary.mesh_seconds` and profiler buckets are included
|
|
only as diagnostics, never as the performance claim.
|
|
|
|
The export comparison is the same `128^3` `TunnelNetwork`, seed `1337`, slot `1`, operator stack,
|
|
step `1`, with three cases in one commandlet process on each side. The 17% reporting floor is
|
|
therefore met by both run count and the retained density-grid result.
|
|
|
|
## Part 1 — block early-out
|
|
|
|
I tested an `8^3` block reject behind conservative fallback logic. On this cave/stack world, every
|
|
one of the 4,096 blocks was left uncertain and went through normal sampling:
|
|
|
|
| batch | runs | end-to-end wall seconds | blocks skipped | skip rate | result |
|
|
| --- | ---: | --- | ---: | ---: | --- |
|
|
| baseline | 3 | 3.805057, 3.522129, 3.569186 (avg 3.632124) | — | — | reference |
|
|
| block reject | 3 | 6.341454, 6.562621, 7.300234 (avg 6.734770) | 0 / 4,096 | 0.00% | **reverted** |
|
|
|
|
The candidate added classification work without skipping a block (+85.42% wall clock). It also
|
|
did not produce a useful supremum for the complete cave stack, so I did not promote a guessed
|
|
`max` parameter into a topology decision. `ClassifyTile` remains untouched: its stack, disturbance,
|
|
diff-layer, and cave guards are not a safe substitute for a new block proof. A false AllSolid
|
|
verdict here would delete a 3D cave, which is not an acceptable trade.
|
|
|
|
This did not clear the floor and was reverted.
|
|
|
|
## Part 2 — density-grid reuse
|
|
|
|
The canonical mesher requests 35^3 samples per 32-cell tile. For 64 tiles that is:
|
|
|
|
- tile requests: `64 * 35^3 = 2,744,000`;
|
|
- unique points in the expanded 128-cell lattice: `131^3 = 2,248,091`;
|
|
- duplicate evaluations: `495,909` (18.07%).
|
|
|
|
The first implementation evaluated the shared lattice as Z planes. It removed all duplicates but
|
|
was slower because it destroyed the generator's spatial locality:
|
|
|
|
| candidate | runs | end-to-end wall seconds | duplicates | result |
|
|
| --- | ---: | --- | --- | --- |
|
|
| matched control | 3 | 3.668954, 3.633445, 3.655658 (avg 3.652686) | 495,909 → 495,909 | reference |
|
|
| global-plane precompute | 3 | 5.349520, 5.352927, 5.510752 (avg 5.404400) | 495,909 → 0 | **reverted** |
|
|
| owner-grid precompute | 3 | 1.950207, 1.973673, 2.161305 (avg 2.028395) | 495,909 → 0 | retained candidate |
|
|
|
|
The retained implementation partitions the shared lattice into 64 disjoint rectangular owner
|
|
regions. Each global sample is generated exactly once, then each tile copies its local halo window
|
|
from the immutable run-scoped grid. The precompute is deliberately inside the wall-clock timer;
|
|
the final three-case repeat measured:
|
|
|
|
| batch | runs | end-to-end wall seconds | average |
|
|
| --- | ---: | --- | ---: |
|
|
| final control (`density_grid_reuse=false`) | 3 | 3.659587, 3.619013, 3.582257 | 3.620286 s |
|
|
| final reuse (`density_grid_reuse=true`) | 3 | 1.908017, 1.854344, 1.884170 | 1.882177 s |
|
|
|
|
That is a **48.01% wall-clock reduction / 1.92x speedup**, clearing the 17% floor. The final
|
|
reuse runs averaged 1.474271 s for the shared-grid build, and still won end to end. The 128^3
|
|
shared lattice is about 8.6 MiB of float storage. Direct explorer runs default to reuse; a batch
|
|
case can pass `density_grid_reuse=false` for a matched control.
|
|
|
|
The run-scoped handoff is confined to the explorer's finite canonical export. The streaming game
|
|
path has no one-shot set of neighboring tiles or invalidation boundary, so it was not given a
|
|
locking plane cache that could serialize workers or outlive a generation epoch.
|
|
|
|
## Part 3 — task granularity
|
|
|
|
The final control observed 15 worker thread IDs and 64 logical tile invocations. Across its three
|
|
runs, the measured tile-job averages were:
|
|
|
|
| metric | average |
|
|
| --- | ---: |
|
|
| fastest logical tile job | 0.188817 s |
|
|
| mean logical tile job | 0.593751 s |
|
|
| slowest logical tile job | 0.955788 s |
|
|
| sum of tile-job durations | 38.000045 s |
|
|
| tile parallel wall | 3.150925 s |
|
|
| launch-fringe estimate | 14.1 us |
|
|
| workers / logical jobs | 15 / 64 |
|
|
|
|
I tested `MinBatchSize=4` with three matched cases: wall seconds `3.590884, 3.634734,
|
|
3.753986` (avg `3.659868`) versus the final control average `3.620286`. The tile parallel bucket
|
|
was slightly lower (`3.125197` s vs `3.150925` s), but end-to-end wall clock was **1.09% slower**.
|
|
The observed worker count and logical job count stayed 15 / 64, and the launch-fringe estimate
|
|
remained about 13.1 us. This is the required bucket-win/no-wall-win case: the tuning was reverted;
|
|
the production/default setting remains `MinBatchSize=1`.
|
|
|
|
`ParallelForWithExistingTaskContext` was not adopted. The measured launch fringe is negligible
|
|
relative to 0.59 s tile work, and the mesher already uses thread-local scratch storage. No claim is
|
|
made here about the separate game workload of many small streaming submissions.
|
|
|
|
## Part 4 — renderer
|
|
|
|
No renderer rewrite or RMC change was justified. The source audit and three final render cases
|
|
showed:
|
|
|
|
- the canonical mesh is built once per explorer world;
|
|
- the triangle cache/BVH is built once and reused by all eight viewpoints;
|
|
- each viewpoint owns one `FColor` output allocation;
|
|
- the per-pixel traversal uses a fixed stack array, not a heap allocation;
|
|
- camera movement does not call the mesher again.
|
|
|
|
The three 512x288 render cases had total case wall seconds `8.704730, 8.985744, 8.517606`
|
|
(avg `8.736027`), including camera-seed walking. Raster-only diagnostics averaged 1.482921 s
|
|
for eight views, 0.286297 s for the first view, 0.170946 s per additional view, and 0.083190 s
|
|
for one-time acceleration construction. This is consistent with the owner's approximately
|
|
0.15 s/view observation; meshing benefits from Part 2 automatically. There was no renderer
|
|
change, so there is no before/after performance claim to report or keep.
|
|
|
|
## Memory/index follow-up
|
|
|
|
The existing profiler research measured 431,162,116 bytes (411.2 MiB) across 15 workers in the
|
|
large run. In the representative 128^3 breakdown, spatial-index bins and candidate arrays alone
|
|
accounted for 214,345,644 bytes, inside a 360,063,124-byte cache total. The index addition did not
|
|
show an end-to-end wall-clock gain in the prior measurements.
|
|
|
|
I did not remove it silently. The explicit follow-up proposal is an index-on/index-off batch
|
|
ablation before deletion: removing it should recover roughly that allocation, at the cost of
|
|
broader room/tunnel candidate scans and potentially slower density queries. That trade is not
|
|
proven by this task's export comparison.
|
|
|
|
## Determinism, invariants, and scope
|
|
|
|
All three final control hashes and all three final reuse hashes are `ED0F1F31`; every case reported
|
|
`json_deterministic=yes`. The three render cases also repeated the same geometry hash
|
|
`847BE4B9`. Owner regions are disjoint, tile results are merged in fixed Z/Y/X order, no RNG or
|
|
runtime CPU-feature dispatch was added, and the shared grid is read-only after its deterministic
|
|
fill. The only worker writes are to distinct cells of that run-owned output lattice; generator and
|
|
world state remain read-only.
|
|
|
|
The build succeeded with UnrealBuildTool after the final source change. Changes are limited to the
|
|
VoxelForge source and this report, with generated evidence under the plugin's `Saved` directory.
|
|
RealtimeMeshComponent was not modified. No VoxelM root, sibling plugin, `Content`, or `Config`
|
|
files were written; no links or historical worktrees were created.
|