73 lines
5.1 KiB
C#
73 lines
5.1 KiB
C#
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();
|
||
}
|