perf #418: widen the destination-lane budget only while the reveal hold is active
While a destination reservation hides the world behind the authored tunnel, the streaming frame meter now runs a hold-widened profile (StreamingWorkBudget.WidenForDestinationHold): the time ceiling rises from the authored 2 ms to an absolute 8 ms default (ACDREAM_STREAM_WORK_HOLD_DEST_MS is a measurement-only override), every count/byte dimension scales by the same factor so elapsed time stays the authoritative guard (the measured binder is Time at both ceilings), and the reserve fraction is re-derived (0.75 -> 0.9375) so the NON-destination lane's absolute per-frame caps are unchanged. The widening keys off the existing BeginDestinationReservation/ EndDestinationReservation bracket only, is derived per-Tick from the CURRENT budget (mid-hold quality swaps compose), and a frame with no reservation uses the authored budget verbatim (test-pinned). Portal holds ride the same bracket as login holds by construction - intended, and pinned by a kind-parity test through the real coordinator plus a live @telepoi portal hold (kind=portal gate-ready 3589 ms). Why: issue #418's next-hypothesis (1). Measured result: the ~5 s publication drip collapsed to ~2 s (loaded 625/625 at ~3.0 s, tunnel at 64-66 fps), the portal-hold gate-ready fell to ~3.6 s - and login gate-ready/total stayed at 8.4-8.8 s / 12.6-12.7 s, exposing the real remaining pacer: the login-cold render-thread upload/registration barrier behind GpuWorldState.IsRenderReady, which ran concurrently under the old drip. Full attribution appended to docs/ISSUES.md #418; no divergence-register row (the streamed result and reveal gate are byte-identical; only the scheduling rate during a hidden hold changed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
e94e8e0c61
commit
a1d15a82dd
5 changed files with 420 additions and 7 deletions
|
|
@ -665,9 +665,21 @@ public sealed class StreamingController
|
|||
throw new InvalidOperationException(
|
||||
"StreamingController.Tick cannot be reentered.");
|
||||
|
||||
// #418: while the destination reservation hides the world behind the
|
||||
// authored tunnel, the frame runs on the hold-widened profile so the
|
||||
// reveal-critical drip is not paced by the authored mid-game budget.
|
||||
// Keyed off the existing reservation bracket only, derived from the
|
||||
// CURRENT budget every frame (so a mid-hold quality swap composes),
|
||||
// and reverting the moment EndDestinationReservation clears the
|
||||
// reservation. A frame with no reservation uses _workBudget verbatim.
|
||||
bool destinationHold = _destinationReservation is not null;
|
||||
var meter = new StreamingWorkMeter(
|
||||
_workBudget,
|
||||
destinationReservationActive: _destinationReservation is not null);
|
||||
destinationHold
|
||||
? _workBudget.WidenForDestinationHold(
|
||||
_configuredWorkBudgetOptions
|
||||
.HoldDestinationCeilingMilliseconds)
|
||||
: _workBudget,
|
||||
destinationReservationActive: destinationHold);
|
||||
_activeWorkMeter = meter;
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -70,6 +70,56 @@ public readonly record struct StreamingWorkBudget
|
|||
nameof(StreamingWorkBudget));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hold-scoped destination-lane widening (#418). While a destination
|
||||
/// reservation is active the world behind the reveal gate is hidden by
|
||||
/// the authored tunnel, so destination publication may spend more
|
||||
/// update-thread time per frame without any visible pop-in. This scales
|
||||
/// the WHOLE profile by <paramref name="ceilingMilliseconds"/> over the
|
||||
/// current time ceiling — every count/byte dimension by the same factor,
|
||||
/// so elapsed time remains the authoritative guard exactly as the
|
||||
/// authored profile intends (measured: the hold's meter yields are
|
||||
/// Time-limit yields; the count/byte dimensions never bound first) —
|
||||
/// and re-derives the reserve fraction so the NON-destination lane's
|
||||
/// absolute per-frame caps are unchanged: with the shipped profile the
|
||||
/// widened caps are floor-exact (e.g. completions
|
||||
/// <c>floor(256 x 0.0625) = floor(64 x 0.25) = 16</c>); a fractional
|
||||
/// measurement override can drift a lane floor by one unit.
|
||||
/// A ceiling at or below the current time ceiling returns this profile
|
||||
/// unchanged — widening never shrinks, and a no-hold frame never calls
|
||||
/// this at all.
|
||||
/// </summary>
|
||||
public StreamingWorkBudget WidenForDestinationHold(
|
||||
double ceilingMilliseconds)
|
||||
{
|
||||
if (!double.IsFinite(ceilingMilliseconds) || ceilingMilliseconds <= 0)
|
||||
return this;
|
||||
|
||||
double scale =
|
||||
ceilingMilliseconds / MaxUpdateTime.TotalMilliseconds;
|
||||
if (scale <= 1.0)
|
||||
return this;
|
||||
|
||||
// The unreserved (non-destination) share shrinks by the same factor
|
||||
// the totals grow, keeping the non-destination lane's absolute caps
|
||||
// at the authored values. Extreme measurement overrides could round
|
||||
// the float to 1.0, which the ctor rejects; the largest float below
|
||||
// one keeps the lane at its Max(1, ...) floor instead.
|
||||
float widenedReserve = (float)(
|
||||
1.0 - (1.0 - DestinationReserveFraction) / scale);
|
||||
if (widenedReserve >= 1f)
|
||||
widenedReserve = MathF.BitDecrement(1f);
|
||||
|
||||
return new StreamingWorkBudget(
|
||||
TimeSpan.FromMilliseconds(ceilingMilliseconds),
|
||||
StreamingWorkBudgetOptions.Scale(MaxCompletionAdmissions, scale),
|
||||
StreamingWorkBudgetOptions.Scale(MaxAdoptedCpuBytes, scale),
|
||||
StreamingWorkBudgetOptions.Scale(MaxEntityOperations, scale),
|
||||
StreamingWorkBudgetOptions.Scale(MaxGpuUploadBytes, scale),
|
||||
StreamingWorkBudgetOptions.Scale(MaxGlRetireOperations, scale),
|
||||
widenedReserve);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,16 @@ namespace AcDream.App.Streaming;
|
|||
/// Startup-time scheduling ceilings for update-thread streaming work.
|
||||
/// These values limit work per frame; they never reduce the amount or
|
||||
/// quality of content that eventually becomes resident.
|
||||
/// <para><paramref name="HoldDestinationCeilingMilliseconds"/> (#418) is the
|
||||
/// hold-scoped destination-lane time ceiling: while a destination
|
||||
/// reservation hides the world behind the authored tunnel, the frame meter
|
||||
/// runs on <see cref="StreamingWorkBudget.WidenForDestinationHold"/> of the
|
||||
/// current profile instead of the profile itself. It is an absolute ceiling
|
||||
/// (not scaled by the legacy quality selector), it never shrinks a profile
|
||||
/// whose own ceiling is already at least as large, and it is NOT a user
|
||||
/// setting — the environment override
|
||||
/// (<c>ACDREAM_STREAM_WORK_HOLD_DEST_MS</c>) exists for A/B measurement
|
||||
/// only, matching the rest of the <c>ACDREAM_STREAM_WORK_*</c> family.</para>
|
||||
/// </summary>
|
||||
public sealed record StreamingWorkBudgetOptions(
|
||||
double MaxUpdateMilliseconds,
|
||||
|
|
@ -14,7 +24,8 @@ public sealed record StreamingWorkBudgetOptions(
|
|||
int MaxEntityOperations,
|
||||
long MaxGpuUploadBytes,
|
||||
int MaxGlRetireOperations,
|
||||
float DestinationReserveFraction)
|
||||
float DestinationReserveFraction,
|
||||
double HoldDestinationCeilingMilliseconds = 8.0)
|
||||
{
|
||||
public const long MiB = 1024L * 1024L;
|
||||
|
||||
|
|
@ -30,7 +41,8 @@ public sealed record StreamingWorkBudgetOptions(
|
|||
MaxEntityOperations: 4_096,
|
||||
MaxGpuUploadBytes: 8 * MiB,
|
||||
MaxGlRetireOperations: 64,
|
||||
DestinationReserveFraction: 0.75f);
|
||||
DestinationReserveFraction: 0.75f,
|
||||
HoldDestinationCeilingMilliseconds: 8.0);
|
||||
|
||||
internal static StreamingWorkBudgetOptions Parse(
|
||||
Func<string, string?> env)
|
||||
|
|
@ -58,7 +70,10 @@ public sealed record StreamingWorkBudgetOptions(
|
|||
defaults.MaxGlRetireOperations),
|
||||
DestinationReserveFraction: ParseReservePercent(
|
||||
env("ACDREAM_STREAM_WORK_DEST_RESERVE_PERCENT"),
|
||||
defaults.DestinationReserveFraction));
|
||||
defaults.DestinationReserveFraction),
|
||||
HoldDestinationCeilingMilliseconds: ParsePositiveDouble(
|
||||
env("ACDREAM_STREAM_WORK_HOLD_DEST_MS"),
|
||||
defaults.HoldDestinationCeilingMilliseconds));
|
||||
}
|
||||
|
||||
public StreamingWorkBudget ToBudget() => new(
|
||||
|
|
@ -75,6 +90,10 @@ public sealed record StreamingWorkBudgetOptions(
|
|||
/// landblock count now selects a complete work profile instead of acting
|
||||
/// as a second hidden execution throttle. Four is the historical High
|
||||
/// profile and therefore preserves this configured profile exactly.
|
||||
/// <see cref="HoldDestinationCeilingMilliseconds"/> deliberately rides
|
||||
/// through unscaled: it is an absolute hold-scoped ceiling, so a smaller
|
||||
/// quality profile widens further toward the same ceiling during a hold
|
||||
/// and a profile already at or above it widens not at all.
|
||||
/// </summary>
|
||||
public StreamingWorkBudgetOptions ScaleForLegacyCompletionCount(int count)
|
||||
{
|
||||
|
|
@ -149,7 +168,7 @@ public sealed record StreamingWorkBudgetOptions(
|
|||
return percent / 100f;
|
||||
}
|
||||
|
||||
private static int Scale(int value, double scale)
|
||||
internal static int Scale(int value, double scale)
|
||||
{
|
||||
if (scale >= int.MaxValue / (double)value)
|
||||
return int.MaxValue;
|
||||
|
|
@ -158,7 +177,7 @@ public sealed record StreamingWorkBudgetOptions(
|
|||
(int)Math.Round(value * scale, MidpointRounding.AwayFromZero));
|
||||
}
|
||||
|
||||
private static long Scale(long value, double scale)
|
||||
internal static long Scale(long value, double scale)
|
||||
{
|
||||
if (scale >= long.MaxValue / (double)value)
|
||||
return long.MaxValue;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue