namespace AyCode.Core.Serializers.Console.Benchmarks; /// /// Common contract for every per-engine, per-I/O-mode benchmark row. Each implementation captures /// one (Engine × IoMode × OptionsPreset) combination — e.g. AcBinary Byte[] FastMode SGen — /// and exposes a uniform Serialize / Deserialize hot-path that the benchmark loop /// drives through warmup + adaptive-iter calibration + measurement. /// /// The default + 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 /// to true so the bench loop skips the Des-phase and routes timing into the RT columns. /// internal interface ISerializerBenchmark { /// Serializer engine — typed enum, see for the human-readable form. BenchmarkEngine Engine { get; } /// I/O mode — typed enum, see for the human-readable form. BenchmarkIoMode IoMode { get; } /// Dispatch mode — typed enum ( / / ). For AcBinary derived from UseGeneratedCode + child-type SGen coverage; non-AcBinary engines report their own native dispatch model. BenchmarkDispatchMode DispatchMode { get; } /// Options preset name — e.g. "FastMode", "Default", "NoIntern", "WithCompression". Stays string because preset names are open-ended (per-instance constructor argument). string OptionsPreset { get; } /// /// CLR type of the order graph this benchmark serializes (e.g. typeof(TestOrder_All_False), /// typeof(TestOrder_All_True)). Per-instance: AcBinary picks variant by options preset /// (), MemPack / MsgPack always use _All_False. /// Concrete benchmarks return typeof(T) for their generic parameter. /// Type OrderType { get; } /// /// Derived display name for the . Default-interface impl reads /// OrderType.Name; 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. /// string OrderTypeName => OrderType.Name; /// Synthesized display name from Engine + IoMode + OptionsPreset. string Name => $"{Engine.ToDisplay()} ({IoMode.ToDisplay()}, {OptionsPreset})"; int SerializedSize { get; } string? OptionsDescription => null; /// 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. long SetupSerializeAllocBytes { get; } /// 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. long SetupDeserializeAllocBytes { get; } /// 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. bool IsRoundTripOnly => false; /// Warm only the Serialize path. Default body iterates N times. /// Overrides are only needed when the implementor wants Ser-specific warmup-state (e.g. pre-allocate buffers). /// On benchmarks (NamedPipe-style) performs the full RT, /// so this warms the entire round-trip path. void WarmupSerialize(int iterations) { for (var i = 0; i < iterations; i++) Serialize(); } /// Warm only the Deserialize path. Default body iterates N times. /// On benchmarks is a no-op, so the bench loop /// skips the Des-phase entirely for those cells. void WarmupDeserialize(int iterations) { for (var i = 0; i < iterations; i++) Deserialize(); } void Serialize(); void Deserialize(); /// Round-trip correctness check — called once per cell before warmup. Returns true if Serialize+Deserialize preserves data. bool VerifyRoundTrip(); }