feat(cordis): 完成阶段5配置与调试工具
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace ShrinkContext.AppAdapter.Editor
|
||||
{
|
||||
public sealed class ShrinkContextBenchmarkReport
|
||||
{
|
||||
internal ShrinkContextBenchmarkReport(int unrelatedFiberCount, int reloadIterations,
|
||||
double notifyMilliseconds, long notificationDispatchCount, long notificationCandidateVisits,
|
||||
long naiveFiberVisits, int consumerApplyCount, int failureIterations,
|
||||
int restoredFailureCount, double restoreMilliseconds, long managedMemoryDeltaBytes)
|
||||
{
|
||||
UnrelatedFiberCount = unrelatedFiberCount;
|
||||
ReloadIterations = reloadIterations;
|
||||
NotifyMilliseconds = notifyMilliseconds;
|
||||
NotificationDispatchCount = notificationDispatchCount;
|
||||
NotificationCandidateVisits = notificationCandidateVisits;
|
||||
NaiveFiberVisits = naiveFiberVisits;
|
||||
ConsumerApplyCount = consumerApplyCount;
|
||||
FailureIterations = failureIterations;
|
||||
RestoredFailureCount = restoredFailureCount;
|
||||
RestoreMilliseconds = restoreMilliseconds;
|
||||
ManagedMemoryDeltaBytes = managedMemoryDeltaBytes;
|
||||
}
|
||||
|
||||
public int UnrelatedFiberCount { get; }
|
||||
public int ReloadIterations { get; }
|
||||
public double NotifyMilliseconds { get; }
|
||||
public long NotificationDispatchCount { get; }
|
||||
public long NotificationCandidateVisits { get; }
|
||||
public long NaiveFiberVisits { get; }
|
||||
public int ConsumerApplyCount { get; }
|
||||
public int FailureIterations { get; }
|
||||
public int RestoredFailureCount { get; }
|
||||
public double RestoreMilliseconds { get; }
|
||||
public long ManagedMemoryDeltaBytes { get; }
|
||||
}
|
||||
|
||||
/// <summary>Editor 可重复容量基准;耗时仅报告,不作为跨机器通过阈值。</summary>
|
||||
public static class ShrinkContextBenchmarkRunner
|
||||
{
|
||||
public static async UniTask<ShrinkContextBenchmarkReport> RunAsync(
|
||||
int unrelatedFiberCount = 1000, int reloadIterations = 100, int failureIterations = 25)
|
||||
{
|
||||
if (unrelatedFiberCount < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(unrelatedFiberCount));
|
||||
if (reloadIterations <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(reloadIterations));
|
||||
if (failureIterations <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(failureIterations));
|
||||
|
||||
var memoryBefore = GC.GetTotalMemory(false);
|
||||
var runtime = new ShrinkContextRuntime();
|
||||
var targetConsumer = new BenchmarkConsumer("benchmark.target");
|
||||
runtime.Use(targetConsumer);
|
||||
for (var i = 0; i < unrelatedFiberCount; i++)
|
||||
runtime.Use(new BenchmarkConsumer("benchmark.unrelated." + i));
|
||||
|
||||
var notifyStopwatch = Stopwatch.StartNew();
|
||||
for (var i = 0; i < reloadIterations; i++)
|
||||
{
|
||||
var provider = runtime.Use(new BenchmarkProvider("provider-" + i, "benchmark.target"));
|
||||
if (provider.LastError != null)
|
||||
throw new InvalidOperationException("Benchmark provider failed.", provider.LastError);
|
||||
await runtime.RetireAsync(provider);
|
||||
}
|
||||
notifyStopwatch.Stop();
|
||||
|
||||
var notifySnapshot = runtime.CaptureDiagnostic();
|
||||
var naiveFiberVisits = notifySnapshot.Notifications.DispatchCount * runtime.Fibers.Count;
|
||||
|
||||
var catalog = new ShrinkComponentCatalog();
|
||||
catalog.Register("stable", () => new BenchmarkProvider("stable", "benchmark.restore"));
|
||||
catalog.Register("failing", () => new BenchmarkFailingProvider("benchmark.restore"));
|
||||
var restoreRuntime = new ShrinkContextRuntime();
|
||||
var loader = new ShrinkContextLoader(restoreRuntime, catalog);
|
||||
await loader.ApplyAsync(new[] { new ShrinkLoaderEntry("provider", "stable") });
|
||||
|
||||
var restoredFailures = 0;
|
||||
var restoreStopwatch = Stopwatch.StartNew();
|
||||
for (var i = 0; i < failureIterations; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
await loader.ApplyAsync(new[] { new ShrinkLoaderEntry("provider", "failing") });
|
||||
}
|
||||
catch (ShrinkLoaderException)
|
||||
{
|
||||
if (loader.LastTransaction?.PreviousCompositionRestored == true &&
|
||||
loader.TryGetFiber("provider", out var restored) &&
|
||||
restored.State == ShrinkFiberState.Active)
|
||||
{
|
||||
restoredFailures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
restoreStopwatch.Stop();
|
||||
|
||||
await runtime.ShutdownAsync();
|
||||
await restoreRuntime.ShutdownAsync();
|
||||
var memoryAfter = GC.GetTotalMemory(false);
|
||||
|
||||
return new ShrinkContextBenchmarkReport(
|
||||
unrelatedFiberCount,
|
||||
reloadIterations,
|
||||
notifyStopwatch.Elapsed.TotalMilliseconds,
|
||||
notifySnapshot.Notifications.DispatchCount,
|
||||
notifySnapshot.Notifications.CandidateVisitCount,
|
||||
naiveFiberVisits,
|
||||
targetConsumer.ApplyCount,
|
||||
failureIterations,
|
||||
restoredFailures,
|
||||
restoreStopwatch.Elapsed.TotalMilliseconds,
|
||||
memoryAfter - memoryBefore);
|
||||
}
|
||||
|
||||
private sealed class BenchmarkProvider : IShrinkComponent
|
||||
{
|
||||
private readonly string[] _provide;
|
||||
|
||||
public BenchmarkProvider(string name, string key)
|
||||
{
|
||||
Name = name;
|
||||
Key = key;
|
||||
_provide = new[] { key };
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
private string Key { get; }
|
||||
public IReadOnlyList<string> Inject => Array.Empty<string>();
|
||||
public IReadOnlyList<string> Provide => _provide;
|
||||
|
||||
public UniTask ApplyAsync(ShrinkCtx ctx, object? config)
|
||||
{
|
||||
ctx.Set(Key, Name);
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BenchmarkConsumer : IShrinkComponent
|
||||
{
|
||||
private readonly string[] _inject;
|
||||
|
||||
public BenchmarkConsumer(string key)
|
||||
{
|
||||
Key = key;
|
||||
_inject = new[] { key };
|
||||
}
|
||||
|
||||
private string Key { get; }
|
||||
public string Name => "consumer:" + Key;
|
||||
public int ApplyCount { get; private set; }
|
||||
public IReadOnlyList<string> Inject => _inject;
|
||||
public IReadOnlyList<string> Provide => Array.Empty<string>();
|
||||
|
||||
public UniTask ApplyAsync(ShrinkCtx ctx, object? config)
|
||||
{
|
||||
ctx.Get<string>(Key);
|
||||
ApplyCount++;
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BenchmarkFailingProvider : IShrinkComponent
|
||||
{
|
||||
private readonly string[] _provide;
|
||||
|
||||
public BenchmarkFailingProvider(string key)
|
||||
{
|
||||
Key = key;
|
||||
_provide = new[] { key };
|
||||
}
|
||||
|
||||
private string Key { get; }
|
||||
public string Name => "benchmark-failing";
|
||||
public IReadOnlyList<string> Inject => Array.Empty<string>();
|
||||
public IReadOnlyList<string> Provide => _provide;
|
||||
|
||||
public UniTask ApplyAsync(ShrinkCtx ctx, object? config)
|
||||
{
|
||||
ctx.Set(Key, Name);
|
||||
throw new InvalidOperationException("Expected benchmark replacement failure.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user