test(app): put every strict-zero site on the probe (#250)

The first commit converted the four members the issue named and left the other
sites alone, reasoning that none had been observed failing. A 20-run
complete-solution baseline disproved that within minutes:

  run  2  LiveEntityRuntimeTests.AnimationView_HotSpatialTraversal…
  run 14  StaticRenderProjectionJournalTests.ActiveAnimatedSynchronization…
  run 18  StaticRenderProjectionJournalTests.ActiveAnimatedSynchronization…
  run 19  CurrentRenderSceneOracleTests.SurfaceOverrideFingerprint…

Both new names are the same shape as the four — one warm call, then a
thousand-iteration loop inside the measured window — and neither had been
recorded anywhere. "Not observed failing" only ever meant "not yet observed",
and leaving known-shape sites in place would have guaranteed the acceptance gate
failed. Run 19 is the sharper lesson: the issue named
`SurfaceOverrideFingerprint_DictionaryHotPathAllocatesNothing`, and the first
commit converted a *different* test in that same file, so the actually-named
member was still on the old shape. Matching by file was not matching by test.

Every strict-zero site in the assembly is now on the probe — ten tests. Two came
out stricter rather than merely steadier:

`StaticRenderProjectionJournalTests` was measuring a synchronise whose journal
does **not** coalesce. Repeating it grew the journal by 1,000 entries per call —
192,000 by the end of a probe run — so the steady state the test claimed to
measure did not exist and the single-call window had been hiding it. Its step is
now the whole frame cycle, synchronise *and* drain, which puts `DrainTo` inside
the measured window for the first time and asserts the journal ends empty.

`RetailInboundEventDispatcherTests` asserted a hard-coded 1,001 callbacks. It
now counts its own dispatches and pins the callback count against that, so the
assertion still proves the fast path ran the callback every time without being
coupled to a loop bound that no longer exists.

Left alone deliberately: the four sites asserting a tolerance rather than zero —
`CellViewDedupTests` and `PortalProjectionTests`. Their ceilings already absorb
this noise and none has flaked; changing a bound in either direction is a
separate decision from fixing a measurement. Worth noting that
`PortalProjectionTests`' ceiling exists explicitly to tolerate "a
tiered-JIT/ArrayPool bookkeeping transition ... to the first measured batch",
which is exactly what the probe removes, so it could probably be tightened to
zero now — recorded in the issue rather than done here.

Solution build 0 warnings / 0 errors; App suite 3,941 passed / 3 skipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-29 03:49:13 +02:00
parent 1d73ce524c
commit 200f19ce47
10 changed files with 116 additions and 82 deletions

View file

@ -484,18 +484,17 @@ public sealed class CurrentRenderSceneOracleTests
CurrentRenderSceneOracle.CreateSurfaceOverrideFingerprint(
overrides);
RenderSceneHash128 actual = default;
long before = GC.GetAllocatedBytesForCurrentThread();
for (int iteration = 0; iteration < 10_000; iteration++)
{
actual =
// #250: the measured window was a 10,000-iteration loop written inline,
// which is exactly the shape on-stack replacement rewrites mid-flight,
// on this thread, inside the window.
ZeroAllocationProbe.AssertAllocatesNothing(
"CurrentRenderSceneOracle.CreateSurfaceOverrideFingerprint",
() => actual =
CurrentRenderSceneOracle.CreateSurfaceOverrideFingerprint(
overrides);
}
overrides));
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Assert.Equal(expected, actual);
Assert.Equal(0, allocated);
}
private static CurrentRenderProjectionFingerprint CaptureSingle(

View file

@ -310,12 +310,11 @@ public sealed class EquippedChildProjectionWithdrawalTests
fixture.Controller.Tick();
fixture.Controller.ReconcileSpatialMutations();
long before = GC.GetAllocatedBytesForCurrentThread();
for (int i = 0; i < 1_000; i++)
fixture.Controller.ReconcileSpatialMutations();
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
// #250: the measured window was a 1,000-iteration loop written inline.
ZeroAllocationProbe.AssertAllocatesNothing(
"LiveEntityController.ReconcileSpatialMutations",
() => fixture.Controller.ReconcileSpatialMutations());
Assert.Equal(0L, allocated);
Assert.Equal(0, fixture.Controller.LastReconcilePoseCompositionVisits);
}

View file

@ -90,13 +90,13 @@ public sealed class RenderFrameRouteOwnerSelectorTests
var owners = new HashSet<uint>();
owners.EnsureCapacity(16);
SelectOwners(owners, in view);
long before = GC.GetAllocatedBytesForCurrentThread();
for (int iteration = 0; iteration < 1_000; iteration++)
SelectOwners(owners, in view);
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
// #250: warmed once, then measured a 1,000-iteration loop written
// inline — the shape on-stack replacement rewrites mid-measurement.
RenderFrameView captured = view;
ZeroAllocationProbe.AssertAllocatesNothing(
"RenderFrameRouteOwnerSelector.SelectOwners",
() => SelectOwners(owners, in captured));
Assert.Equal(0, allocated);
Assert.Equal([50u], owners);
exchange.Release(in view);
}

View file

@ -358,19 +358,34 @@ public sealed class StaticRenderProjectionJournalTests
}
statics.SynchronizeActiveAnimatedSources(animated);
journal.DrainTo(scene);
for (int index = 0; index < animated.Length; index++)
{
animated[index].Rotation = Quaternion.CreateFromAxisAngle(
Vector3.UnitZ,
0.5f);
}
long before = GC.GetAllocatedBytesForCurrentThread();
// #250: one warm call is not enough to have paid the callee tree's
// first-call costs. The step is the whole steady-state frame cycle —
// advance the rotations, synchronise, drain — rather than a bare
// synchronise, for two reasons. The rotations must actually change on
// every invocation or the synchronise early-outs and the probe measures
// nothing; and the journal does not coalesce, so without the drain it
// grows by `count` entries per invocation and the thing under test
// stops being steady state at all. Draining also makes this stricter
// than the original: `DrainTo` is now inside the measured window.
float angle = 0.5f;
ZeroAllocationProbe.AssertAllocatesNothing(
"StaticRenderProjectionJournal animated synchronise and drain",
() =>
{
angle += 0.01f;
for (int index = 0; index < animated.Length; index++)
{
animated[index].Rotation = Quaternion.CreateFromAxisAngle(
Vector3.UnitZ,
angle);
}
statics.SynchronizeActiveAnimatedSources(animated);
statics.SynchronizeActiveAnimatedSources(animated);
journal.DrainTo(scene);
});
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Assert.Equal(count, journal.Count);
Assert.True(allocated == 0, $"Allocated {allocated:N0} bytes.");
// Retained storage: the cycle leaves nothing behind to grow.
Assert.Equal(0, journal.Count);
}
private static GpuLandblockSpatialPublication Publication(

View file

@ -171,16 +171,10 @@ public sealed class PackedProjectionClassificationCacheTests
cache.CompleteRebuild(entry, reusableAcrossFrames: true);
cache.EndFrame();
for (int i = 0; i < 100; i++)
Hit(cache, generation, id, identity);
long before = GC.GetAllocatedBytesForCurrentThread();
for (int i = 0; i < 1_000; i++)
Hit(cache, generation, id, identity);
long allocated =
GC.GetAllocatedBytesForCurrentThread() - before;
Assert.Equal(0, allocated);
// #250: the measured window was a 1,000-iteration loop written inline.
ZeroAllocationProbe.AssertAllocatesNothing(
"PackedProjectionClassificationCache warm cache hit",
() => Hit(cache, generation, id, identity));
}
private static void Hit(

View file

@ -84,15 +84,15 @@ public sealed class GpuWorldStateRenderTraversalTests
var bounds = state.LandblockBounds;
_ = entries.Count;
_ = bounds.Count;
long before = GC.GetAllocatedBytesForCurrentThread();
for (int iteration = 0; iteration < 1_000; iteration++)
{
_ = state.LandblockEntries.Count;
_ = state.LandblockBounds.Count;
}
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
// #250: the measured window was a 1,000-iteration loop written inline.
ZeroAllocationProbe.AssertAllocatesNothing(
"GpuWorldState landblock collection access",
() =>
{
_ = state.LandblockEntries.Count;
_ = state.LandblockBounds.Count;
});
Assert.Equal(0, allocated);
Assert.Same(entries, state.LandblockEntries);
Assert.Same(bounds, state.LandblockBounds);

View file

@ -69,11 +69,9 @@ public sealed class UiTextLayoutCacheTests
Func<IReadOnlyList<UiText.Line>> provider = cache.Provider;
_ = provider();
long before = GC.GetAllocatedBytesForCurrentThread();
for (int i = 0; i < 1_000; i++)
_ = provider();
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Assert.Equal(0L, allocated);
// #250: the measured window was a 1,000-iteration loop written inline.
ZeroAllocationProbe.AssertAllocatesNothing(
"UiTextLayoutCache stable provider poll",
() => _ = provider());
}
}

View file

@ -556,18 +556,20 @@ public sealed class LiveEntityRuntimeTests
view.CopySpatialIdsTo(ids);
foreach (KeyValuePair<uint, AnimationRuntime> _ in view) { }
// #250: the old shape warmed once and then measured a 1,000-iteration
// loop written inline, which is precisely the shape on-stack
// replacement rewrites mid-flight, on this thread, inside the window.
int visits = 0;
long before = GC.GetAllocatedBytesForCurrentThread();
for (int i = 0; i < 1_000; i++)
{
view.CopySpatialIdsTo(ids);
foreach (KeyValuePair<uint, AnimationRuntime> _ in view)
visits++;
}
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
ZeroAllocationProbe.AssertAllocatesNothing(
"LiveEntityAnimationRuntimeView spatial traversal",
() =>
{
view.CopySpatialIdsTo(ids);
foreach (KeyValuePair<uint, AnimationRuntime> _ in view)
visits++;
});
Assert.Equal(0, allocated);
Assert.Equal(1_000, visits);
Assert.True(visits > 0);
Assert.Equal(entity.Id, Assert.Single(ids));
}

View file

@ -99,14 +99,19 @@ public sealed class RetailInboundEventDispatcherTests
static (target, amount) => target.Value += amount;
dispatcher.Run(counter, 1, increment);
long before = GC.GetAllocatedBytesForCurrentThread();
for (int i = 0; i < 1_000; i++)
{
dispatcher.Run(counter, 1, increment);
}
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
// #250: the measured window was a 1,000-iteration loop written inline.
int dispatches = 0;
ZeroAllocationProbe.AssertAllocatesNothing(
"RetailInboundEventDispatcher.Run state fast path",
() =>
{
dispatches++;
dispatcher.Run(counter, 1, increment);
});
Assert.Equal(0, allocated);
Assert.Equal(1_001, counter.Value);
// The fast path really ran the callback on every dispatch — once for
// the priming call above, then once per probe invocation.
Assert.True(dispatches > 0);
Assert.Equal(dispatches + 1, counter.Value);
}
}