AyCode.Core/AyCode.Benchmark/Reporting/BenchmarkResult.cs

87 lines
5.2 KiB
C#

using AyCode.Core.Benchmarks.Workloads.Scenarios;
namespace AyCode.Core.Benchmarks.Reporting;
/// <summary>
/// Per-cell benchmark result row. Populated by the benchmark execution loop (Console-side
/// <c>BenchmarkLoop.RunBenchmarksForTestData</c> / BDN-side <c>BdnSummaryAdapter</c>); consumed by the
/// output formatters in <c>BenchmarkReportWriter</c> (console table + .log + .LLM file writers).
/// Pure DTO — no behaviour.
/// </summary>
public sealed class BenchmarkResult
{
public string TestDataName { get; set; } = "";
public BenchmarkEngine Engine { get; set; }
public BenchmarkIoMode IoMode { get; set; }
public BenchmarkDispatchMode DispatchMode { get; set; }
public string OptionsPreset { get; set; } = "";
/// <summary>
/// CLR type name of the order graph serialised in this row (e.g. <c>"TestOrder_All_False"</c>,
/// <c>"TestOrder_All_True"</c>). Captured from <see cref="ISerializerBenchmark.OrderTypeName"/> in
/// the runner loop; surfaced in the SERIALIZER OPTIONS section of every output
/// (.log, .LLM, console) so the reader can correlate each preset with its TestOrder variant
/// without inflating the per-row tables with an extra column.
/// </summary>
public string OrderTypeName { get; set; } = "";
/// <summary>True if Serialize() captures a full round-trip and Deserialize() is a no-op
/// (single-use streaming transports like NamedPipe). Excluded from "Fastest Serialize" / "Fastest Deserialize"
/// winners rankings; still ranked in "Fastest Round-trip". Display-side: Ser µs/op / SerAlloc / Des µs/op / DesAlloc
/// all show "N/A" since they were never measured separately; RT µs/op / RT Alloc carry the full round-trip values.</summary>
public bool IsRoundTripOnly { get; set; }
/// <summary>Synthesized display name for backwards compatibility / single-string-row scenarios. Includes DispatchMode so SGen and Runtime variants of the same preset don't collide in grouping (e.g. SUMMARY: WINNERS).</summary>
public string SerializerName => $"{Engine.ToDisplay()} ({IoMode.ToDisplay()}, {OptionsPreset}, {DispatchMode.ToDisplay()})";
public string? OptionsDescription { get; set; }
public int SerializedSize { get; set; }
public double SerializeTimeMs { get; set; }
public double DeserializeTimeMs { get; set; }
// Per-sample min/max alongside the median (median is the *Time*Ms field above). Surfaces
// inter-sample range — the visible noise floor for the row. 0 when the operation was skipped
// (mode != "all"/"ser"/"des") or when a single-sample fast path was used (min == max == median).
public double SerializeTimeMinMs { get; set; }
public double SerializeTimeMaxMs { get; set; }
public double DeserializeTimeMinMs { get; set; }
public double DeserializeTimeMaxMs { get; set; }
// Sample-population stddev (ms). Used by FormatMicrosWithRange to compute CV (stddev/mean)
// and emit the ⚠️ marker on rows above Configuration.UnstableCVThreshold. 0 in single-sample mode.
public double SerializeTimeStdDevMs { get; set; }
public double DeserializeTimeStdDevMs { get; set; }
// Per-row adaptive iteration count (post-CalibrateIterations). Each Ser and Des function calibrates
// independently to land its sample window at ~Configuration.TargetSampleMs; per-op µs is then iter-independent
// (`SerializeTimeMs / SerializeIterations * 1000`). For round-trip-only rows (NamedPipe etc.),
// RoundTripIterations carries the calibrated iter count; SerializeIterations and DeserializeIterations
// stay 0 (Ser and Des are not separately measurable on those rows).
public int SerializeIterations { get; set; }
public int DeserializeIterations { get; set; }
public int RoundTripIterations { get; set; }
public long SerializeAllocBytesPerOp { get; set; }
public long DeserializeAllocBytesPerOp { get; set; }
public long SetupSerializeAllocBytes { get; set; }
public long SetupDeserializeAllocBytes { get; set; }
/// <summary>Total round-trip time. For in-memory benchmarks: synthesized so that
/// <c>RoundTripTimeMs / RoundTripIterations</c> yields the correct <c>SerPerOp + DesPerOp</c> µs/op
/// (necessary because Ser and Des may have different iter counts post-calibration).
/// For round-trip-only benchmarks (NamedPipe etc.): the directly-measured pipe round-trip time.</summary>
public double RoundTripTimeMs { get; set; }
// Round-trip min/max + stddev — only populated for round-trip-only benchmarks (NamedPipe etc.) where
// RT is directly measured. For in-memory rows RT = Ser + Des, which has no single-sample
// distribution; surface Ser/Des range separately instead.
public double RoundTripTimeMinMs { get; set; }
public double RoundTripTimeMaxMs { get; set; }
public double RoundTripTimeStdDevMs { get; set; }
/// <summary>Total round-trip allocation per op. For in-memory benchmarks: <c>SerializeAlloc + DeserializeAlloc</c>.
/// For round-trip-only benchmarks: process-wide allocation measured via <see cref="GC.GetTotalAllocatedBytes"/>
/// (covers ALL threads — client, server-drain, channel internals — not just the caller).</summary>
public long RoundTripAllocBytesPerOp { get; set; }
}