feat(streaming): establish cost budget ledger
Add the validated frame-work profile, deterministic completion charges, and shadow admission meter before enforcing the scheduler in Slice E2. Lifecycle artifacts now expose elapsed work, would-yield limits, backlog bytes/age, and pending owner ledgers without changing accepted execution. Tests: dotnet build AcDream.slnx -c Release --no-restore; dotnet test AcDream.slnx -c Release --no-build --no-restore (8124 passed, 5 skipped)
This commit is contained in:
parent
2945896a6f
commit
ac45cb1bd7
14 changed files with 1280 additions and 71 deletions
121
src/AcDream.App/Streaming/StreamingWorkBudgetOptions.cs
Normal file
121
src/AcDream.App/Streaming/StreamingWorkBudgetOptions.cs
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace AcDream.App.Streaming;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public sealed record StreamingWorkBudgetOptions(
|
||||
double MaxUpdateMilliseconds,
|
||||
int MaxCompletionAdmissions,
|
||||
long MaxAdoptedCpuBytes,
|
||||
int MaxEntityOperations,
|
||||
long MaxGpuUploadBytes,
|
||||
int MaxGlRetireOperations,
|
||||
float DestinationReserveFraction)
|
||||
{
|
||||
public const long MiB = 1024L * 1024L;
|
||||
|
||||
public static StreamingWorkBudgetOptions Default { get; } = new(
|
||||
MaxUpdateMilliseconds: 2.0,
|
||||
MaxCompletionAdmissions: 64,
|
||||
MaxAdoptedCpuBytes: 8 * MiB,
|
||||
MaxEntityOperations: 256,
|
||||
MaxGpuUploadBytes: 8 * MiB,
|
||||
MaxGlRetireOperations: 64,
|
||||
DestinationReserveFraction: 0.75f);
|
||||
|
||||
internal static StreamingWorkBudgetOptions Parse(
|
||||
Func<string, string?> env)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(env);
|
||||
StreamingWorkBudgetOptions defaults = Default;
|
||||
return new StreamingWorkBudgetOptions(
|
||||
MaxUpdateMilliseconds: ParsePositiveDouble(
|
||||
env("ACDREAM_STREAM_WORK_MS"),
|
||||
defaults.MaxUpdateMilliseconds),
|
||||
MaxCompletionAdmissions: ParsePositiveInt(
|
||||
env("ACDREAM_STREAM_WORK_COMPLETIONS"),
|
||||
defaults.MaxCompletionAdmissions),
|
||||
MaxAdoptedCpuBytes: ParseMiB(
|
||||
env("ACDREAM_STREAM_WORK_CPU_MIB"),
|
||||
defaults.MaxAdoptedCpuBytes),
|
||||
MaxEntityOperations: ParsePositiveInt(
|
||||
env("ACDREAM_STREAM_WORK_ENTITY_OPS"),
|
||||
defaults.MaxEntityOperations),
|
||||
MaxGpuUploadBytes: ParseMiB(
|
||||
env("ACDREAM_STREAM_WORK_GPU_MIB"),
|
||||
defaults.MaxGpuUploadBytes),
|
||||
MaxGlRetireOperations: ParsePositiveInt(
|
||||
env("ACDREAM_STREAM_WORK_GL_RETIRE_OPS"),
|
||||
defaults.MaxGlRetireOperations),
|
||||
DestinationReserveFraction: ParseReservePercent(
|
||||
env("ACDREAM_STREAM_WORK_DEST_RESERVE_PERCENT"),
|
||||
defaults.DestinationReserveFraction));
|
||||
}
|
||||
|
||||
public StreamingWorkBudget ToBudget() => new(
|
||||
TimeSpan.FromMilliseconds(MaxUpdateMilliseconds),
|
||||
MaxCompletionAdmissions,
|
||||
MaxAdoptedCpuBytes,
|
||||
MaxEntityOperations,
|
||||
MaxGpuUploadBytes,
|
||||
MaxGlRetireOperations,
|
||||
DestinationReserveFraction);
|
||||
|
||||
private static double ParsePositiveDouble(string? value, double fallback) =>
|
||||
double.TryParse(
|
||||
value,
|
||||
NumberStyles.Float,
|
||||
CultureInfo.InvariantCulture,
|
||||
out double parsed)
|
||||
&& double.IsFinite(parsed)
|
||||
&& parsed > 0
|
||||
? parsed
|
||||
: fallback;
|
||||
|
||||
private static int ParsePositiveInt(string? value, int fallback) =>
|
||||
int.TryParse(
|
||||
value,
|
||||
NumberStyles.Integer,
|
||||
CultureInfo.InvariantCulture,
|
||||
out int parsed)
|
||||
&& parsed > 0
|
||||
? parsed
|
||||
: fallback;
|
||||
|
||||
private static long ParseMiB(string? value, long fallback)
|
||||
{
|
||||
if (!long.TryParse(
|
||||
value,
|
||||
NumberStyles.Integer,
|
||||
CultureInfo.InvariantCulture,
|
||||
out long mebibytes)
|
||||
|| mebibytes <= 0
|
||||
|| mebibytes > long.MaxValue / MiB)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return checked(mebibytes * MiB);
|
||||
}
|
||||
|
||||
private static float ParseReservePercent(string? value, float fallback)
|
||||
{
|
||||
if (!float.TryParse(
|
||||
value,
|
||||
NumberStyles.Float,
|
||||
CultureInfo.InvariantCulture,
|
||||
out float percent)
|
||||
|| !float.IsFinite(percent)
|
||||
|| percent <= 0f
|
||||
|| percent >= 100f)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return percent / 100f;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue