59 lines
3.8 KiB
C#
59 lines
3.8 KiB
C#
namespace AyCode.Core.Serializers.Console.Benchmarks;
|
||
|
||
/// <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>
|
||
internal interface ISerializerBenchmark
|
||
{
|
||
/// <summary>Serializer engine — e.g. "AcBinary", "MemoryPack", "MessagePack".</summary>
|
||
string Engine { get; }
|
||
/// <summary>I/O mode — e.g. "Byte[]", "BufWr reuse", "BufWr new", "NamedPipe", "FileStream".</summary>
|
||
string IoMode { get; }
|
||
/// <summary>Dispatch mode — "SGen", "Runtime", or "Hybrid". For AcBinary derived from <c>UseGeneratedCode</c> + child-type SGen coverage; non-AcBinary engines report their own native dispatch model.</summary>
|
||
string DispatchMode { get; }
|
||
/// <summary>Options preset name — e.g. "FastMode", "Default", "NoIntern", "WithCompression".</summary>
|
||
string OptionsPreset { get; }
|
||
/// <summary>Synthesized display name from Engine + IoMode + OptionsPreset.</summary>
|
||
string Name => $"{Engine} ({IoMode}, {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();
|
||
}
|