AyCode.Core/AyCode.Benchmark/Workloads/Scenarios/ISerializerBenchmark.cs

73 lines
5.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace AyCode.Core.Benchmarks.Workloads.Scenarios;
/// <summary>
/// Common contract for every per-engine, per-I/O-mode benchmark row. Each implementation captures
/// one (Engine × IoMode × OptionsPreset) combination — e.g. <c>AcBinary Byte[] FastMode SGen</c> —
/// and exposes a uniform <c>Serialize</c> / <c>Deserialize</c> hot-path that the benchmark loop
/// drives through warmup + adaptive-iter calibration + measurement.
///
/// <para>The default <see cref="WarmupSerialize"/> + <see cref="WarmupDeserialize"/> methods iterate
/// the hot path N times — overrides are only needed when an implementor wants Ser/Des-specific
/// warmup state (rare). Round-trip-only benchmarks (NamedPipe etc.) set <see cref="IsRoundTripOnly"/>
/// to true so the bench loop skips the Des-phase and routes timing into the RT columns.</para>
/// </summary>
public interface ISerializerBenchmark
{
/// <summary>Serializer engine — typed enum, see <see cref="BenchmarkEnumExtensions.ToDisplay(BenchmarkEngine)"/> for the human-readable form.</summary>
BenchmarkEngine Engine { get; }
/// <summary>I/O mode — typed enum, see <see cref="BenchmarkEnumExtensions.ToDisplay(BenchmarkIoMode)"/> for the human-readable form.</summary>
BenchmarkIoMode IoMode { get; }
/// <summary>Dispatch mode — typed enum (<see cref="BenchmarkDispatchMode.SGen"/> / <see cref="BenchmarkDispatchMode.Runtime"/> / <see cref="BenchmarkDispatchMode.Hybrid"/>). For AcBinary derived from <c>UseGeneratedCode</c> + child-type SGen coverage; non-AcBinary engines report their own native dispatch model.</summary>
BenchmarkDispatchMode DispatchMode { get; }
/// <summary>Options preset name — e.g. "FastMode", "Default", "NoIntern", "WithCompression". Stays string because preset names are open-ended (per-instance constructor argument).</summary>
string OptionsPreset { get; }
/// <summary>
/// CLR type of the order graph this benchmark serializes (e.g. <c>typeof(TestOrder_All_False)</c>,
/// <c>typeof(TestOrder_All_True)</c>). Per-instance: AcBinary picks variant by options preset
/// (caller-side dispatch rule), MemPack / MsgPack always use <c>_All_False</c>.
/// Concrete benchmarks return <c>typeof(T)</c> for their generic parameter.
/// </summary>
Type OrderType { get; }
/// <summary>
/// Derived display name for the <see cref="OrderType"/>. Default-interface impl reads
/// <c>OrderType.Name</c>; concrete classes don't need to override. Surfaced in the SERIALIZER
/// OPTIONS section of every output (.log, .LLM, console) — not in the per-row tables — so the
/// reader correlates each preset with its TestOrder variant without inflating the result columns.
/// </summary>
string OrderTypeName => OrderType.Name;
/// <summary>Synthesized display name from Engine + IoMode + OptionsPreset.</summary>
string Name => $"{Engine.ToDisplay()} ({IoMode.ToDisplay()}, {OptionsPreset})";
int SerializedSize { get; }
string? OptionsDescription => null;
/// <summary>One-time SERIALIZER-side setup allocation cost (e.g., pre-allocated ArrayBufferWriter with internal buffer). Captured in constructor; 0 for byte[] API and Fresh-BufWriter variants.</summary>
long SetupSerializeAllocBytes { get; }
/// <summary>One-time DESERIALIZER-side setup allocation cost (e.g., long-lived AsyncPipeReaderInput's ArrayPool rent + ManualResetEventSlim, drain-task scaffolding). Captured in constructor; 0 for byte[] API and any setup-free deserialize path.</summary>
long SetupDeserializeAllocBytes { get; }
/// <summary>True when Serialize() does a full round-trip (e.g. NamedPipe) and Deserialize() is a no-op.
/// Used by the SUMMARY: WINNERS section to skip such cells from "Fastest Serialize" and "Fastest Deserialize"
/// rankings (because both metrics are misleading there) — they still participate in "Fastest Round-trip".
/// Default false for in-memory IO modes which measure Ser and Des separately.</summary>
bool IsRoundTripOnly => false;
/// <summary>Warm only the Serialize path. Default body iterates <see cref="Serialize"/> N times.
/// Overrides are only needed when the implementor wants Ser-specific warmup-state (e.g. pre-allocate buffers).
/// On <see cref="IsRoundTripOnly"/> benchmarks (NamedPipe-style) <see cref="Serialize"/> performs the full RT,
/// so this warms the entire round-trip path.</summary>
void WarmupSerialize(int iterations)
{
for (var i = 0; i < iterations; i++) Serialize();
}
/// <summary>Warm only the Deserialize path. Default body iterates <see cref="Deserialize"/> N times.
/// On <see cref="IsRoundTripOnly"/> benchmarks <see cref="Deserialize"/> is a no-op, so the bench loop
/// skips the Des-phase entirely for those cells.</summary>
void WarmupDeserialize(int iterations)
{
for (var i = 0; i < iterations; i++) Deserialize();
}
void Serialize();
void Deserialize();
/// <summary>Round-trip correctness check — called once per cell before warmup. Returns true if Serialize+Deserialize preserves data.</summary>
bool VerifyRoundTrip();
}