using System.Globalization; using Xunit.Abstractions; namespace NATS.Server.Benchmark.Tests.Harness; /// /// Writes side-by-side benchmark comparison output to xUnit's ITestOutputHelper. /// public static class BenchmarkResultWriter { public static void WriteComparison(ITestOutputHelper output, BenchmarkResult goResult, BenchmarkResult dotnetResult) { var ratio = dotnetResult.MessagesPerSecond / goResult.MessagesPerSecond; output.WriteLine($"=== {goResult.Name} ==="); output.WriteLine($"Go: {FormatRate(goResult.MessagesPerSecond)} msg/s | {goResult.MegabytesPerSecond:F1} MB/s | {goResult.Duration.TotalMilliseconds:F0} ms"); output.WriteLine($".NET: {FormatRate(dotnetResult.MessagesPerSecond)} msg/s | {dotnetResult.MegabytesPerSecond:F1} MB/s | {dotnetResult.Duration.TotalMilliseconds:F0} ms"); output.WriteLine($"Ratio: {ratio:F2}x (.NET / Go)"); if (goResult.Latencies is not null && dotnetResult.Latencies is not null) { output.WriteLine(""); output.WriteLine("Latency (us):"); output.WriteLine($" {"",8} {"P50",10} {"P95",10} {"P99",10} {"Min",10} {"Max",10}"); WriteLatencyRow(output, "Go", goResult.Latencies); WriteLatencyRow(output, ".NET", dotnetResult.Latencies); } output.WriteLine(""); } public static void WriteSingle(ITestOutputHelper output, BenchmarkResult result) { output.WriteLine($"=== {result.Name} ({result.ServerType}) ==="); output.WriteLine($"{FormatRate(result.MessagesPerSecond)} msg/s | {result.MegabytesPerSecond:F1} MB/s | {result.Duration.TotalMilliseconds:F0} ms"); if (result.Latencies is not null) { output.WriteLine(""); output.WriteLine("Latency (us):"); output.WriteLine($" {"",8} {"P50",10} {"P95",10} {"P99",10} {"Min",10} {"Max",10}"); WriteLatencyRow(output, result.ServerType, result.Latencies); } output.WriteLine(""); } private static void WriteLatencyRow(ITestOutputHelper output, string label, LatencyPercentiles p) { output.WriteLine($" {label,8} {p.P50Us,10:F1} {p.P95Us,10:F1} {p.P99Us,10:F1} {p.MinUs,10:F1} {p.MaxUs,10:F1}"); } private static string FormatRate(double rate) => rate.ToString("N0", CultureInfo.InvariantCulture).PadLeft(15); }