From eaafb00739d2e8b435c4f1a4698f30bf643a02e7 Mon Sep 17 00:00:00 2001 From: Loretta Date: Wed, 13 May 2026 05:58:34 +0200 Subject: [PATCH] [LOADED_DOCS: 2 files, no new loads] Refactor benchmarks to use typed enums for engine/mode Replaced string-based identifiers for serializer engine, I/O mode, and dispatch mode with strongly-typed enums (BenchmarkEngine, BenchmarkIoMode, BenchmarkDispatchMode). Added BenchmarkEnums.cs with ToDisplay() helpers for consistent output. Updated all benchmark implementations, DTOs, and output logic to use enums. Removed obsolete string constants from Configuration.cs. Merged allocation measurement methods in BenchmarkLoop.cs for clarity. Improves type safety, maintainability, and output consistency. --- .../BenchmarkLoop.cs | 41 +++------ .../BenchmarkResult.cs | 10 ++- .../Benchmarks/AcBinaryBenchmark.cs | 6 +- .../AcBinaryBufferWriterBenchmark.cs | 6 +- .../AcBinaryFreshBufferWriterBenchmark.cs | 6 +- .../AcBinaryInMemoryPipeBenchmark.cs | 6 +- .../AcBinaryInMemoryRawByteArrayBenchmark.cs | 6 +- .../Benchmarks/AcBinaryNamedPipeBenchmark.cs | 6 +- .../AcBinaryNamedPipeRawByteArrayBenchmark.cs | 6 +- .../Benchmarks/BenchmarkEnums.cs | 90 +++++++++++++++++++ .../Benchmarks/ISerializerBenchmark.cs | 16 ++-- .../Benchmarks/MemoryPackBenchmark.cs | 6 +- .../MemoryPackBufferWriterBenchmark.cs | 6 +- .../MemoryPackFreshBufferWriterBenchmark.cs | 6 +- .../Benchmarks/MessagePackBenchmark.cs | 6 +- .../Benchmarks/SystemTextJsonBenchmark.cs | 6 +- .../Configuration.cs | 27 +----- AyCode.Core.Serializers.Console/Output.cs | 59 ++++++------ 18 files changed, 182 insertions(+), 133 deletions(-) create mode 100644 AyCode.Core.Serializers.Console/Benchmarks/BenchmarkEnums.cs diff --git a/AyCode.Core.Serializers.Console/BenchmarkLoop.cs b/AyCode.Core.Serializers.Console/BenchmarkLoop.cs index 13fc83b..7ce5ee8 100644 --- a/AyCode.Core.Serializers.Console/BenchmarkLoop.cs +++ b/AyCode.Core.Serializers.Console/BenchmarkLoop.cs @@ -227,7 +227,7 @@ internal static class BenchmarkLoop result.RoundTripIterations = rtIter; // Process-wide allocation measurement: server-drain-thread allocations (server-side new byte[len]) // also show up — otherwise current-thread alloc would only count the client side and look ~halved. - result.RoundTripAllocBytesPerOp = MeasureAllocationTotal(() => serializer.Serialize(), rtIter, $"{groupLabel} [RT alloc]"); + result.RoundTripAllocBytesPerOp = MeasureAllocation(() => serializer.Serialize(), rtIter, $"{groupLabel} [RT alloc]", processWide: true); } // mode == "deserialize" alone is meaningless for a round-trip-only benchmark; skip silently. } @@ -611,43 +611,24 @@ internal static class BenchmarkLoop } /// - /// Measures per-call allocation in bytes after a clean GC. Single dedicated sample (no median) — keeps timing samples pure. + /// Measures per-call allocation in bytes after a clean GC. Single dedicated sample (no median) — keeps + /// timing samples pure. When is true, uses + /// instead of + /// — needed for round-trip-only benchmarks (NamedPipe etc.) where the work happens across multiple + /// threads (server-side new byte[len] buffers, drain-pump-thread allocations). Per-thread mode + /// is slightly cleaner for in-memory benchmarks; process-wide mode is slightly noisier (background + /// threads / GC bookkeeping leak in) but over 1000 iterations the signal dominates. /// - internal static long MeasureAllocation(Action action, int iterations, string? progressLabel = null) + internal static long MeasureAllocation(Action action, int iterations, string? progressLabel = null, bool processWide = false) { GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); var sw = Stopwatch.StartNew(); - var before = GC.GetAllocatedBytesForCurrentThread(); + var before = processWide ? GC.GetTotalAllocatedBytes(precise: true) : GC.GetAllocatedBytesForCurrentThread(); RunWithProgress(action, iterations, progressLabel, samples: 1, sampleIndex: 0); - - var after = GC.GetAllocatedBytesForCurrentThread(); - sw.Stop(); - EndProgress(progressLabel, sw.Elapsed.TotalMilliseconds); - return (after - before) / iterations; - } - - /// - /// Process-wide allocation measurement — needed for round-trip-only benchmarks (NamedPipe etc.) where - /// the work happens across multiple threads. would - /// only count the caller-thread allocations, missing the server-side new byte[len] buffers and - /// any drain-pump-thread allocations. covers the entire process. - /// Slightly noisier than the per-thread variant (background threads / GC bookkeeping leak in), but - /// over 1000 iterations the signal dominates. - /// - internal static long MeasureAllocationTotal(Action action, int iterations, string? progressLabel = null) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - var sw = Stopwatch.StartNew(); - var before = GC.GetTotalAllocatedBytes(precise: true); - RunWithProgress(action, iterations, progressLabel, samples: 1, sampleIndex: 0); - - var after = GC.GetTotalAllocatedBytes(precise: true); + var after = processWide ? GC.GetTotalAllocatedBytes(precise: true) : GC.GetAllocatedBytesForCurrentThread(); sw.Stop(); EndProgress(progressLabel, sw.Elapsed.TotalMilliseconds); return (after - before) / iterations; diff --git a/AyCode.Core.Serializers.Console/BenchmarkResult.cs b/AyCode.Core.Serializers.Console/BenchmarkResult.cs index 7f80c20..6b60dbd 100644 --- a/AyCode.Core.Serializers.Console/BenchmarkResult.cs +++ b/AyCode.Core.Serializers.Console/BenchmarkResult.cs @@ -1,3 +1,5 @@ +using AyCode.Core.Serializers.Console.Benchmarks; + namespace AyCode.Core.Serializers.Console; /// @@ -8,9 +10,9 @@ namespace AyCode.Core.Serializers.Console; internal sealed class BenchmarkResult { public string TestDataName { get; set; } = ""; - public string Engine { get; set; } = ""; - public string IoMode { get; set; } = ""; - public string DispatchMode { get; set; } = ""; + public BenchmarkEngine Engine { get; set; } + public BenchmarkIoMode IoMode { get; set; } + public BenchmarkDispatchMode DispatchMode { get; set; } public string OptionsPreset { get; set; } = ""; /// True if Serialize() captures a full round-trip and Deserialize() is a no-op @@ -20,7 +22,7 @@ internal sealed class BenchmarkResult public bool IsRoundTripOnly { get; set; } /// 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). - public string SerializerName => $"{Engine} ({IoMode}, {OptionsPreset}, {DispatchMode})"; + public string SerializerName => $"{Engine.ToDisplay()} ({IoMode.ToDisplay()}, {OptionsPreset}, {DispatchMode.ToDisplay()})"; public string? OptionsDescription { get; set; } public int SerializedSize { get; set; } diff --git a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryBenchmark.cs index e77a187..1bd4883 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryBenchmark.cs @@ -14,9 +14,9 @@ internal sealed class AcBinaryBenchmark : ISerializerBenchmark private readonly AcBinarySerializerOptions _options; private readonly byte[] _serialized; - public string Engine => Configuration.EngineAcBinary; - public string IoMode => Configuration.IoByteArray; - public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; + public BenchmarkEngine Engine => BenchmarkEngine.AcBinary; + public BenchmarkIoMode IoMode => BenchmarkIoMode.ByteArray; + public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime; public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes => 0; diff --git a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryBufferWriterBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryBufferWriterBenchmark.cs index aacafe1..58415b2 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryBufferWriterBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryBufferWriterBenchmark.cs @@ -16,9 +16,9 @@ internal sealed class AcBinaryBufferWriterBenchmark : ISerializerBenchmark private readonly byte[] _serialized; private readonly ArrayBufferWriter _bufferWriter; - public string Engine => Configuration.EngineAcBinary; - public string IoMode => Configuration.IoBufWrReuse; - public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; + public BenchmarkEngine Engine => BenchmarkEngine.AcBinary; + public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrReuse; + public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime; public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes { get; } diff --git a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryFreshBufferWriterBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryFreshBufferWriterBenchmark.cs index 2e43f42..420ceb9 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryFreshBufferWriterBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryFreshBufferWriterBenchmark.cs @@ -18,9 +18,9 @@ internal sealed class AcBinaryFreshBufferWriterBenchmark : ISerializerBenchmark private readonly AcBinarySerializerOptions _options; private readonly byte[] _serialized; - public string Engine => Configuration.EngineAcBinary; - public string IoMode => Configuration.IoBufWrNew; - public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; + public BenchmarkEngine Engine => BenchmarkEngine.AcBinary; + public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrNew; + public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime; public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes => 0; diff --git a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryInMemoryPipeBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryInMemoryPipeBenchmark.cs index 1b09377..363e7cb 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryInMemoryPipeBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryInMemoryPipeBenchmark.cs @@ -47,9 +47,9 @@ internal sealed class AcBinaryInMemoryPipeBenchmark : ISerializerBenchmark, IDis private bool _captureResult; private bool _disposed; - public string Engine => Configuration.EngineAcBinary; - public string IoMode => Configuration.IoInMemoryPipe; - public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; + public BenchmarkEngine Engine => BenchmarkEngine.AcBinary; + public BenchmarkIoMode IoMode => BenchmarkIoMode.InMemoryPipe; + public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime; public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes { get; } diff --git a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryInMemoryRawByteArrayBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryInMemoryRawByteArrayBenchmark.cs index ccde326..d2e305a 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryInMemoryRawByteArrayBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryInMemoryRawByteArrayBenchmark.cs @@ -38,9 +38,9 @@ internal sealed class AcBinaryInMemoryRawByteArrayBenchmark : ISerializerBenchma private bool _captureResult; private bool _disposed; - public string Engine => Configuration.EngineAcBinary; - public string IoMode => Configuration.IoInMemoryRaw; - public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; + public BenchmarkEngine Engine => BenchmarkEngine.AcBinary; + public BenchmarkIoMode IoMode => BenchmarkIoMode.InMemoryRaw; + public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime; public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes { get; } diff --git a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryNamedPipeBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryNamedPipeBenchmark.cs index 9065cfc..99bd6c7 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryNamedPipeBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryNamedPipeBenchmark.cs @@ -62,9 +62,9 @@ internal sealed class AcBinaryNamedPipeBenchmark : ISerializerBenchmark, IDispos private bool _captureResult; // toggle: when true, ConsumeLoop stores result; otherwise discards private bool _disposed; - public string Engine => Configuration.EngineAcBinary; - public string IoMode => Configuration.IoNamedPipe; - public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; + public BenchmarkEngine Engine => BenchmarkEngine.AcBinary; + public BenchmarkIoMode IoMode => BenchmarkIoMode.NamedPipe; + public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime; public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes { get; } diff --git a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryNamedPipeRawByteArrayBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryNamedPipeRawByteArrayBenchmark.cs index d6b49ec..6f85d79 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryNamedPipeRawByteArrayBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/AcBinaryNamedPipeRawByteArrayBenchmark.cs @@ -49,9 +49,9 @@ internal sealed class AcBinaryNamedPipeRawByteArrayBenchmark : ISerializerBenchm private bool _captureResult; // toggle: when true, ConsumerLoop stores result; otherwise discards private bool _disposed; - public string Engine => Configuration.EngineAcBinary; - public string IoMode => Configuration.IoNamedPipeRaw; - public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; + public BenchmarkEngine Engine => BenchmarkEngine.AcBinary; + public BenchmarkIoMode IoMode => BenchmarkIoMode.NamedPipeRaw; + public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime; public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes { get; } diff --git a/AyCode.Core.Serializers.Console/Benchmarks/BenchmarkEnums.cs b/AyCode.Core.Serializers.Console/Benchmarks/BenchmarkEnums.cs new file mode 100644 index 0000000..3e4fc04 --- /dev/null +++ b/AyCode.Core.Serializers.Console/Benchmarks/BenchmarkEnums.cs @@ -0,0 +1,90 @@ +namespace AyCode.Core.Serializers.Console.Benchmarks; + +/// +/// Serializer engine identifier — replaces the prior Configuration.EngineXxx string constants +/// with a type-safe enum. The benchmark-result Engine column uses for +/// the human-readable form. +/// +internal enum BenchmarkEngine +{ + AcBinary, + MemoryPack, +#if !AYCODE_NATIVEAOT + MessagePack, +#endif + SystemTextJson, +} + +/// +/// I/O mode identifier — replaces the prior Configuration.IoXxx string constants. Note that +/// and share the display string "NamedPipe" +/// (they distinguish chunked-framed vs raw-byte[] semantics, but render identically in the IO column); +/// the same applies to + ("Pipe(in-mem)"). +/// +internal enum BenchmarkIoMode +{ + ByteArray, + BufWrReuse, + BufWrNew, + String, + NamedPipe, + NamedPipeRaw, + InMemoryPipe, + InMemoryRaw, +} + +/// +/// Dispatch mode identifier — replaces the prior Configuration.ModeXxx string constants. +/// Describes how property access / type dispatch happens for a given benchmark row: +/// +/// — compile-time source generator path (Unsafe.As<T> direct fields, slot-array wrapper lookup). +/// — reflection / compiled-delegate path. +/// — SGen root with non-SGen child types reached via bridge methods (see docs/BINARY/BINARY_SGEN.md). +/// +/// +internal enum BenchmarkDispatchMode +{ + SGen, + Runtime, + Hybrid, +} + +/// +/// Display-string converters for the benchmark enums. Renders enum values into the column-friendly +/// human-readable form used by the per-row console table, the .log file CSV/formatted output, +/// and the .LLM markdown table. Centralised here so every output formatter renders identically. +/// +internal static class BenchmarkEnumExtensions +{ + internal static string ToDisplay(this BenchmarkEngine engine) => engine switch + { + BenchmarkEngine.AcBinary => "AcBinary", + BenchmarkEngine.MemoryPack => "MemoryPack", +#if !AYCODE_NATIVEAOT + BenchmarkEngine.MessagePack => "MessagePack", +#endif + BenchmarkEngine.SystemTextJson => "System.Text.Json", + _ => throw new ArgumentOutOfRangeException(nameof(engine), engine, null), + }; + + internal static string ToDisplay(this BenchmarkIoMode mode) => mode switch + { + BenchmarkIoMode.ByteArray => "Byte[]", + BenchmarkIoMode.BufWrReuse => "BufWr reuse", + BenchmarkIoMode.BufWrNew => "BufWr new", + BenchmarkIoMode.String => "String", + BenchmarkIoMode.NamedPipe => "NamedPipe", + BenchmarkIoMode.NamedPipeRaw => "NamedPipe", + BenchmarkIoMode.InMemoryPipe => "Pipe(in-mem)", + BenchmarkIoMode.InMemoryRaw => "Pipe(in-mem)", + _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null), + }; + + internal static string ToDisplay(this BenchmarkDispatchMode mode) => mode switch + { + BenchmarkDispatchMode.SGen => "SGen", + BenchmarkDispatchMode.Runtime => "Runtime", + BenchmarkDispatchMode.Hybrid => "Hybrid", + _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null), + }; +} diff --git a/AyCode.Core.Serializers.Console/Benchmarks/ISerializerBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/ISerializerBenchmark.cs index ed39f22..2215e24 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/ISerializerBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/ISerializerBenchmark.cs @@ -13,16 +13,16 @@ namespace AyCode.Core.Serializers.Console.Benchmarks; /// internal interface ISerializerBenchmark { - /// Serializer engine — e.g. "AcBinary", "MemoryPack", "MessagePack". - string Engine { get; } - /// I/O mode — e.g. "Byte[]", "BufWr reuse", "BufWr new", "NamedPipe", "FileStream". - string IoMode { get; } - /// Dispatch mode — "SGen", "Runtime", or "Hybrid". For AcBinary derived from UseGeneratedCode + child-type SGen coverage; non-AcBinary engines report their own native dispatch model. - string DispatchMode { get; } - /// Options preset name — e.g. "FastMode", "Default", "NoIntern", "WithCompression". + /// 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; } /// Synthesized display name from Engine + IoMode + OptionsPreset. - string Name => $"{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. diff --git a/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackBenchmark.cs index 52444e3..1606ed5 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackBenchmark.cs @@ -15,9 +15,9 @@ internal sealed class MemoryPackBenchmark : ISerializerBenchmark private readonly MemoryPackSerializerOptions _options; private readonly byte[] _serialized; - public string Engine => Configuration.EngineMemoryPack; - public string IoMode => Configuration.IoByteArray; - public string DispatchMode => Configuration.ModeSGen; // MemoryPack always uses [MemoryPackable] source-generated formatters + public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack; + public BenchmarkIoMode IoMode => BenchmarkIoMode.ByteArray; + public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes => 0; diff --git a/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackBufferWriterBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackBufferWriterBenchmark.cs index 0320e6f..e61aa7f 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackBufferWriterBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackBufferWriterBenchmark.cs @@ -17,9 +17,9 @@ internal sealed class MemoryPackBufferWriterBenchmark : ISerializerBenchmark private readonly byte[] _serialized; private readonly ArrayBufferWriter _bufferWriter; - public string Engine => Configuration.EngineMemoryPack; - public string IoMode => Configuration.IoBufWrReuse; - public string DispatchMode => Configuration.ModeSGen; // MemoryPack always uses [MemoryPackable] source-generated formatters + public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack; + public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrReuse; + public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes { get; } diff --git a/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackFreshBufferWriterBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackFreshBufferWriterBenchmark.cs index 032f21a..e69cde4 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackFreshBufferWriterBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/MemoryPackFreshBufferWriterBenchmark.cs @@ -15,9 +15,9 @@ internal sealed class MemoryPackFreshBufferWriterBenchmark : ISerializerBenchmar private readonly MemoryPackSerializerOptions _options; private readonly byte[] _serialized; - public string Engine => Configuration.EngineMemoryPack; - public string IoMode => Configuration.IoBufWrNew; - public string DispatchMode => Configuration.ModeSGen; // MemoryPack always uses [MemoryPackable] source-generated formatters + public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack; + public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrNew; + public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes => 0; diff --git a/AyCode.Core.Serializers.Console/Benchmarks/MessagePackBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/MessagePackBenchmark.cs index 7f4e2b0..af2d67b 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/MessagePackBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/MessagePackBenchmark.cs @@ -18,9 +18,9 @@ internal sealed class MessagePackBenchmark : ISerializerBenchmark private readonly MessagePackSerializerOptions _options; private readonly byte[] _serialized; - public string Engine => Configuration.EngineMessagePack; - public string IoMode => Configuration.IoByteArray; - public string DispatchMode => Configuration.ModeSGen; // MessagePack uses [MessagePackObject] source-generated formatters (StandardResolver) + public BenchmarkEngine Engine => BenchmarkEngine.MessagePack; + public BenchmarkIoMode IoMode => BenchmarkIoMode.ByteArray; + public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MessagePack uses [MessagePackObject] source-generated formatters (StandardResolver) public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes => 0; diff --git a/AyCode.Core.Serializers.Console/Benchmarks/SystemTextJsonBenchmark.cs b/AyCode.Core.Serializers.Console/Benchmarks/SystemTextJsonBenchmark.cs index 2acca54..2d45ccc 100644 --- a/AyCode.Core.Serializers.Console/Benchmarks/SystemTextJsonBenchmark.cs +++ b/AyCode.Core.Serializers.Console/Benchmarks/SystemTextJsonBenchmark.cs @@ -17,9 +17,9 @@ internal sealed class SystemTextJsonBenchmark : ISerializerBenchmark private readonly string _serialized; private readonly byte[] _serializedUtf8; - public string Engine => Configuration.EngineSystemTextJson; - public string IoMode => Configuration.IoString; - public string DispatchMode => Configuration.ModeRuntime; // System.Text.Json default uses reflection-based metadata (no source generator opt-in here) + public BenchmarkEngine Engine => BenchmarkEngine.SystemTextJson; + public BenchmarkIoMode IoMode => BenchmarkIoMode.String; + public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.Runtime; // System.Text.Json default uses reflection-based metadata (no source generator opt-in here) public string OptionsPreset { get; } public int SerializedSize => _serializedUtf8.Length; public long SetupSerializeAllocBytes => 0; diff --git a/AyCode.Core.Serializers.Console/Configuration.cs b/AyCode.Core.Serializers.Console/Configuration.cs index d7290de..1e52e64 100644 --- a/AyCode.Core.Serializers.Console/Configuration.cs +++ b/AyCode.Core.Serializers.Console/Configuration.cs @@ -41,24 +41,7 @@ internal static class Configuration // 1 = Compact, 2 = Fast internal static WireMode SelectedWireMode = WireMode.Compact; - // Serializer name constants - // Engine identifiers (used in Engine column + comparison logic) - internal const string EngineAcBinary = "AcBinary"; - internal const string EngineMemoryPack = "MemoryPack"; -#if !AYCODE_NATIVEAOT - internal const string EngineMessagePack = "MessagePack"; -#endif - internal const string EngineSystemTextJson = "System.Text.Json"; - - // IO mode identifiers (used in IO column + comparison logic) - internal const string IoByteArray = "Byte[]"; - internal const string IoBufWrReuse = "BufWr reuse"; - internal const string IoBufWrNew = "BufWr new"; - internal const string IoString = "String"; - internal const string IoNamedPipe = "NamedPipe"; - internal const string IoNamedPipeRaw = "NamedPipe"; - internal const string IoInMemoryPipe = "Pipe(in-mem)"; - internal const string IoInMemoryRaw = "Pipe(in-mem)"; + // Engine / IO mode / Dispatch mode identifiers → Benchmarks/BenchmarkEnums.cs (typed enums with ToDisplay) // Single source of truth for the chunk size used by ALL pipe-related benchmarks (NamedPipe PipeChunk, // NamedPipe PipeRaw, in-memory Pipe, in-memory RawMem) AND the NamedPipe server's inBufferSize/outBufferSize. @@ -67,14 +50,6 @@ internal static class Configuration // overrides across individual benchmark rows. internal const int PipeChunkSize = 4096; - // Dispatch mode identifiers — describes how property access / type dispatch happens for a given run. - // SGen = compile-time source generator path (Unsafe.As direct fields, slot-array wrapper lookup). - // Runtime= reflection / compiled-delegate path. - // Hybrid = SGen root with non-SGen child types reached via bridge methods. See docs/BINARY/BINARY_SGEN.md. - internal const string ModeSGen = "SGen"; - internal const string ModeRuntime = "Runtime"; - internal const string ModeHybrid = "Hybrid"; - // Per-cell adaptive iteration target wall-clock duration. Each Ser/Des function calibrates its // own iteration count post-warmup so the sample batch lands in this range — equalizes the // per-sample window across cells of vastly different per-op cost (Small ~6 ns/op vs Large diff --git a/AyCode.Core.Serializers.Console/Output.cs b/AyCode.Core.Serializers.Console/Output.cs index e6247d5..1cccfcc 100644 --- a/AyCode.Core.Serializers.Console/Output.cs +++ b/AyCode.Core.Serializers.Console/Output.cs @@ -1,4 +1,5 @@ using AyCode.Core.Serializers.Binaries; +using AyCode.Core.Serializers.Console.Benchmarks; using AyCode.Core.Tests.TestModels; using System.Globalization; using System.Runtime.CompilerServices; @@ -191,10 +192,10 @@ internal static class Output // Order by per-op µs (iter-independent) — rows may have different iter counts post-calibration. var testResults = results.Where(r => r.TestDataName == testData.DisplayName).OrderBy(r => RtPerOp(r)).ToList(); // Baseline switched MessagePack → MemoryPack: MemoryPack is the SOTA performance leader. - var memPackResult = testResults.FirstOrDefault(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray)); + var memPackResult = testResults.FirstOrDefault(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray)); // Pin the comparison to AcBinary's SGen variant — apples-to-apples vs MemoryPack (also source-generated). // The Runtime variant is shown alongside in the table for context, not used as the headline number. - var acBinaryResult = testResults.FirstOrDefault(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen)); + var acBinaryResult = testResults.FirstOrDefault(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen)); System.Console.WriteLine($"\n┌─ {testData.DisplayName} ─".PadRight(172, '─') + "┐"); // Header-only units; per-row entries are numbers (µs/op for time, KB/op for alloc, KB pair "ser / des" for Setup, B for Size). @@ -215,8 +216,8 @@ internal static class Output // Highlight MemoryPack baseline (any Byte[]) and AcBinary headline contender (Byte[] + SGen) with win/lose colors. // The AcBinary Byte[]+Runtime variant is shown unhighlighted — it's contextual (SGen speed-up reference), not the headline. - var isHighlighted = (result.Engine == Configuration.EngineMemoryPack && result.IoMode == Configuration.IoByteArray) - || (result.Engine == Configuration.EngineAcBinary && result.IoMode == Configuration.IoByteArray && result.DispatchMode == Configuration.ModeSGen); + var isHighlighted = (result.Engine == BenchmarkEngine.MemoryPack && result.IoMode == BenchmarkIoMode.ByteArray) + || (result.Engine == BenchmarkEngine.AcBinary && result.IoMode == BenchmarkIoMode.ByteArray && result.DispatchMode == BenchmarkDispatchMode.SGen); var prefix = isHighlighted ? "│►" : "│ "; var suffix = isHighlighted ? "◄│" : " │"; @@ -224,7 +225,7 @@ internal static class Output // Color logic: Green = winner (faster), Red = loser (slower) if (isHighlighted && memPackResult != null && acBinaryResult != null) { - var isMemPack = (result.Engine == Configuration.EngineMemoryPack && result.IoMode == Configuration.IoByteArray); + var isMemPack = (result.Engine == BenchmarkEngine.MemoryPack && result.IoMode == BenchmarkIoMode.ByteArray); var memPackFaster = RtPerOp(memPackResult) < RtPerOp(acBinaryResult); if (isMemPack) @@ -237,7 +238,7 @@ internal static class Output } } - System.Console.WriteLine($"{prefix}{rank++,4} │ {result.Engine,-11} │ {result.OptionsPreset,-22} │ {result.IoMode,-12} │ {result.DispatchMode,-8} │ {setup,14} │ {size,8} │ {ser,10} │ {serAlloc,10} │ {des,10} │ {desAlloc,10} │ {rt,10} │ {rtAlloc,10}{suffix}"); + System.Console.WriteLine($"{prefix}{rank++,4} │ {result.Engine.ToDisplay(),-11} │ {result.OptionsPreset,-22} │ {result.IoMode.ToDisplay(),-12} │ {result.DispatchMode.ToDisplay(),-8} │ {setup,14} │ {size,8} │ {ser,10} │ {serAlloc,10} │ {des,10} │ {desAlloc,10} │ {rt,10} │ {rtAlloc,10}{suffix}"); if (isHighlighted) { @@ -366,13 +367,13 @@ internal static class Output System.Console.WriteLine($"{"Fastest Round-trip",-20} │ {fastestRt.Name,-40} │ {fastestRt.AvgPerOp,12:F2} µs/op"); // Overall AcBinary (SGen) vs MemoryPack comparison. - var memPackSerResults = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.SerializeTimeMs > 0).ToList(); - var memPackDesResults = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.DeserializeTimeMs > 0).ToList(); - var memPackRtResults = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.RoundTripTimeMs > 0).ToList(); + var memPackSerResults = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray) && r.SerializeTimeMs > 0).ToList(); + var memPackDesResults = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray) && r.DeserializeTimeMs > 0).ToList(); + var memPackRtResults = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray) && r.RoundTripTimeMs > 0).ToList(); - var acBinarySerResults = results.Where(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.SerializeTimeMs > 0).ToList(); - var acBinaryDesResults = results.Where(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.DeserializeTimeMs > 0).ToList(); - var acBinaryRtResults = results.Where(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.RoundTripTimeMs > 0).ToList(); + var acBinarySerResults = results.Where(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen) && r.SerializeTimeMs > 0).ToList(); + var acBinaryDesResults = results.Where(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen) && r.DeserializeTimeMs > 0).ToList(); + var acBinaryRtResults = results.Where(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen) && r.RoundTripTimeMs > 0).ToList(); // Skip comparison if no data available if (memPackRtResults.Count == 0 || acBinaryRtResults.Count == 0) @@ -384,8 +385,8 @@ internal static class Output } // All averages are over per-op µs (iter-independent). Three aggregations per metric. - var sizeAcResults = results.Where(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen)).ToList(); - var sizeMpResults = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray)).ToList(); + var sizeAcResults = results.Where(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen)).ToList(); + var sizeMpResults = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray)).ToList(); var serStats = ComputeOverallStats(acBinarySerResults, memPackSerResults, SerPerOp); var desStats = ComputeOverallStats(acBinaryDesResults, memPackDesResults, DesPerOp); @@ -472,7 +473,7 @@ internal static class Output var testResults = results.Where(r => r.TestDataName == testData.DisplayName).ToList(); foreach (var result in testResults) { - sb.AppendLine($"{result.TestDataName},{result.Engine},{result.IoMode},{result.DispatchMode},{result.OptionsPreset},{result.SerializedSize},{SerPerOp(result):F2},{DesPerOp(result):F2},{RtPerOp(result):F2},{result.SerializeAllocBytesPerOp},{result.DeserializeAllocBytesPerOp},{result.RoundTripAllocBytesPerOp},{result.SetupSerializeAllocBytes},{result.SetupDeserializeAllocBytes}"); + sb.AppendLine($"{result.TestDataName},{result.Engine.ToDisplay()},{result.IoMode.ToDisplay()},{result.DispatchMode.ToDisplay()},{result.OptionsPreset},{result.SerializedSize},{SerPerOp(result):F2},{DesPerOp(result):F2},{RtPerOp(result):F2},{result.SerializeAllocBytesPerOp},{result.DeserializeAllocBytesPerOp},{result.RoundTripAllocBytesPerOp},{result.SetupSerializeAllocBytes},{result.SetupDeserializeAllocBytes}"); } } sb.AppendLine(); @@ -486,8 +487,8 @@ internal static class Output { // Order by per-op µs (iter-independent) — rows may have different iter counts post-calibration. var testResults = results.Where(r => r.TestDataName == testData.DisplayName).OrderBy(r => RtPerOp(r)).ToList(); - var memPackResult = testResults.FirstOrDefault(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray)); - var acBinaryResult = testResults.FirstOrDefault(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen)); + var memPackResult = testResults.FirstOrDefault(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray)); + var acBinaryResult = testResults.FirstOrDefault(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen)); sb.AppendLine(); sb.AppendLine($"--- {testData.DisplayName} ---"); @@ -497,7 +498,7 @@ internal static class Output var rank = 1; foreach (var result in testResults) { - var isHighlighted = ((result.Engine == Configuration.EngineMemoryPack || result.Engine == Configuration.EngineAcBinary) && result.IoMode == Configuration.IoByteArray); + var isHighlighted = ((result.Engine == BenchmarkEngine.MemoryPack || result.Engine == BenchmarkEngine.AcBinary) && result.IoMode == BenchmarkIoMode.ByteArray); var prefix = isHighlighted ? "► " : " "; var size = $"{result.SerializedSize:N0}"; @@ -527,13 +528,13 @@ internal static class Output sb.AppendLine(); sb.AppendLine("=== AcBinary (Byte[], SGen) vs MemoryPack (Byte[]) (Overall) ==="); - var memPackSerResults2 = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.SerializeTimeMs > 0).ToList(); - var memPackDesResults2 = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.DeserializeTimeMs > 0).ToList(); - var memPackRtResults2 = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.RoundTripTimeMs > 0).ToList(); + var memPackSerResults2 = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray) && r.SerializeTimeMs > 0).ToList(); + var memPackDesResults2 = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray) && r.DeserializeTimeMs > 0).ToList(); + var memPackRtResults2 = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray) && r.RoundTripTimeMs > 0).ToList(); - var acBinarySerResults2 = results.Where(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.SerializeTimeMs > 0).ToList(); - var acBinaryDesResults2 = results.Where(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.DeserializeTimeMs > 0).ToList(); - var acBinaryRtResults2 = results.Where(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.RoundTripTimeMs > 0).ToList(); + var acBinarySerResults2 = results.Where(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen) && r.SerializeTimeMs > 0).ToList(); + var acBinaryDesResults2 = results.Where(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen) && r.DeserializeTimeMs > 0).ToList(); + var acBinaryRtResults2 = results.Where(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen) && r.RoundTripTimeMs > 0).ToList(); // Skip comparison block if either side has no Byte[] data if (memPackRtResults2.Count == 0 || acBinaryRtResults2.Count == 0) @@ -547,8 +548,8 @@ internal static class Output return; } - var sizeAcResults2 = results.Where(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen)).ToList(); - var sizeMpResults2 = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray)).ToList(); + var sizeAcResults2 = results.Where(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen)).ToList(); + var sizeMpResults2 = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray)).ToList(); AppendOverallLine(sb, "Serialize", "µs/op", ComputeOverallStats(acBinarySerResults2, memPackSerResults2, SerPerOp)); AppendOverallLine(sb, "Ser Alloc", "B/op", ComputeOverallStats(acBinarySerResults2, memPackSerResults2, r => r.SerializeAllocBytesPerOp), "F0"); @@ -620,13 +621,13 @@ internal static class Output var iterCol = r.IsRoundTripOnly ? r.RoundTripIterations.ToString(inv) : $"{(r.SerializeIterations > 0 ? r.SerializeIterations.ToString(inv) : "-")} / {(r.DeserializeIterations > 0 ? r.DeserializeIterations.ToString(inv) : "-")}"; - sb.AppendLine($"{r.TestDataName} | {r.Engine} | {r.IoMode} | {r.DispatchMode} | {r.OptionsPreset} | {r.SerializedSize} | {ser} | {des} | {rt} | {serAlloc} | {desAlloc} | {rtAlloc} | {setupAlloc} | {iterCol}"); + sb.AppendLine($"{r.TestDataName} | {r.Engine.ToDisplay()} | {r.IoMode.ToDisplay()} | {r.DispatchMode.ToDisplay()} | {r.OptionsPreset} | {r.SerializedSize} | {ser} | {des} | {rt} | {serAlloc} | {desAlloc} | {rtAlloc} | {setupAlloc} | {iterCol}"); } } // Overall AcBinary (SGen, Byte[]) vs MemoryPack (Byte[]) comparison - var memPackByteArrayResults = results.Where(r => r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray).ToList(); - var acBinarySGenByteArrayResults = results.Where(r => r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen).ToList(); + var memPackByteArrayResults = results.Where(r => r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray).ToList(); + var acBinarySGenByteArrayResults = results.Where(r => r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen).ToList(); var memPackSerResultsLlm = memPackByteArrayResults.Where(r => r.SerializeTimeMs > 0).ToList(); var memPackDesResultsLlm = memPackByteArrayResults.Where(r => r.DeserializeTimeMs > 0).ToList(); var memPackRtResultsLlm = memPackByteArrayResults.Where(r => r.RoundTripTimeMs > 0).ToList();