using System.Diagnostics; namespace NATS.Server.Benchmark.Tests.Harness; /// /// Lightweight benchmark runner with warmup + timed measurement. /// public sealed class BenchmarkRunner { public int WarmupCount { get; init; } = 1_000; public int MeasurementCount { get; init; } = 100_000; /// /// Measures throughput for a fire-and-forget style workload (pub-only or pub+sub). /// The is called times. /// public async Task MeasureThroughputAsync( string name, string serverType, int payloadSize, Func action) { // Warmup for (var i = 0; i < WarmupCount; i++) await action(i); // Measurement var sw = Stopwatch.StartNew(); for (var i = 0; i < MeasurementCount; i++) await action(i); sw.Stop(); return new BenchmarkResult { Name = name, ServerType = serverType, TotalMessages = MeasurementCount, TotalBytes = (long)MeasurementCount * payloadSize, Duration = sw.Elapsed, }; } /// /// Measures latency for a request-reply style workload. /// Records per-iteration round-trip time and computes percentiles. /// public async Task MeasureLatencyAsync( string name, string serverType, int payloadSize, Func roundTripAction) { // Warmup for (var i = 0; i < WarmupCount; i++) await roundTripAction(i); // Measurement with per-iteration timing var tracker = new LatencyTracker(MeasurementCount); var overallSw = Stopwatch.StartNew(); for (var i = 0; i < MeasurementCount; i++) { var start = Stopwatch.GetTimestamp(); await roundTripAction(i); tracker.Record(Stopwatch.GetTimestamp() - start); } overallSw.Stop(); return new BenchmarkResult { Name = name, ServerType = serverType, TotalMessages = MeasurementCount, TotalBytes = (long)MeasurementCount * payloadSize, Duration = overallSw.Elapsed, Latencies = tracker.ComputePercentiles(), }; } }