[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.
This commit is contained in:
Loretta 2026-05-13 05:58:34 +02:00
parent ad9e05413c
commit eaafb00739
18 changed files with 182 additions and 133 deletions

View File

@ -227,7 +227,7 @@ internal static class BenchmarkLoop
result.RoundTripIterations = rtIter; result.RoundTripIterations = rtIter;
// Process-wide allocation measurement: server-drain-thread allocations (server-side new byte[len]) // 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. // 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. // mode == "deserialize" alone is meaningless for a round-trip-only benchmark; skip silently.
} }
@ -611,43 +611,24 @@ internal static class BenchmarkLoop
} }
/// <summary> /// <summary>
/// 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 <paramref name="processWide"/> is <c>true</c>, uses
/// <see cref="GC.GetTotalAllocatedBytes"/> instead of <see cref="GC.GetAllocatedBytesForCurrentThread"/>
/// — needed for round-trip-only benchmarks (NamedPipe etc.) where the work happens across multiple
/// threads (server-side <c>new byte[len]</c> 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.
/// </summary> /// </summary>
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.Collect();
GC.WaitForPendingFinalizers(); GC.WaitForPendingFinalizers();
GC.Collect(); GC.Collect();
var sw = Stopwatch.StartNew(); 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); RunWithProgress(action, iterations, progressLabel, samples: 1, sampleIndex: 0);
var after = processWide ? GC.GetTotalAllocatedBytes(precise: true) : GC.GetAllocatedBytesForCurrentThread();
var after = GC.GetAllocatedBytesForCurrentThread();
sw.Stop();
EndProgress(progressLabel, sw.Elapsed.TotalMilliseconds);
return (after - before) / iterations;
}
/// <summary>
/// Process-wide allocation measurement — needed for round-trip-only benchmarks (NamedPipe etc.) where
/// the work happens across multiple threads. <see cref="GC.GetAllocatedBytesForCurrentThread"/> would
/// only count the caller-thread allocations, missing the server-side <c>new byte[len]</c> buffers and
/// any drain-pump-thread allocations. <see cref="GC.GetTotalAllocatedBytes"/> covers the entire process.
/// Slightly noisier than the per-thread variant (background threads / GC bookkeeping leak in), but
/// over 1000 iterations the signal dominates.
/// </summary>
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);
sw.Stop(); sw.Stop();
EndProgress(progressLabel, sw.Elapsed.TotalMilliseconds); EndProgress(progressLabel, sw.Elapsed.TotalMilliseconds);
return (after - before) / iterations; return (after - before) / iterations;

View File

@ -1,3 +1,5 @@
using AyCode.Core.Serializers.Console.Benchmarks;
namespace AyCode.Core.Serializers.Console; namespace AyCode.Core.Serializers.Console;
/// <summary> /// <summary>
@ -8,9 +10,9 @@ namespace AyCode.Core.Serializers.Console;
internal sealed class BenchmarkResult internal sealed class BenchmarkResult
{ {
public string TestDataName { get; set; } = ""; public string TestDataName { get; set; } = "";
public string Engine { get; set; } = ""; public BenchmarkEngine Engine { get; set; }
public string IoMode { get; set; } = ""; public BenchmarkIoMode IoMode { get; set; }
public string DispatchMode { get; set; } = ""; public BenchmarkDispatchMode DispatchMode { get; set; }
public string OptionsPreset { get; set; } = ""; public string OptionsPreset { get; set; } = "";
/// <summary>True if Serialize() captures a full round-trip and Deserialize() is a no-op /// <summary>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; } public bool IsRoundTripOnly { get; set; }
/// <summary>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).</summary> /// <summary>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).</summary>
public string SerializerName => $"{Engine} ({IoMode}, {OptionsPreset}, {DispatchMode})"; public string SerializerName => $"{Engine.ToDisplay()} ({IoMode.ToDisplay()}, {OptionsPreset}, {DispatchMode.ToDisplay()})";
public string? OptionsDescription { get; set; } public string? OptionsDescription { get; set; }
public int SerializedSize { get; set; } public int SerializedSize { get; set; }

View File

@ -14,9 +14,9 @@ internal sealed class AcBinaryBenchmark : ISerializerBenchmark
private readonly AcBinarySerializerOptions _options; private readonly AcBinarySerializerOptions _options;
private readonly byte[] _serialized; private readonly byte[] _serialized;
public string Engine => Configuration.EngineAcBinary; public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
public string IoMode => Configuration.IoByteArray; public BenchmarkIoMode IoMode => BenchmarkIoMode.ByteArray;
public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes => 0; public long SetupSerializeAllocBytes => 0;

View File

@ -16,9 +16,9 @@ internal sealed class AcBinaryBufferWriterBenchmark : ISerializerBenchmark
private readonly byte[] _serialized; private readonly byte[] _serialized;
private readonly ArrayBufferWriter<byte> _bufferWriter; private readonly ArrayBufferWriter<byte> _bufferWriter;
public string Engine => Configuration.EngineAcBinary; public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
public string IoMode => Configuration.IoBufWrReuse; public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrReuse;
public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes { get; } public long SetupSerializeAllocBytes { get; }

View File

@ -18,9 +18,9 @@ internal sealed class AcBinaryFreshBufferWriterBenchmark : ISerializerBenchmark
private readonly AcBinarySerializerOptions _options; private readonly AcBinarySerializerOptions _options;
private readonly byte[] _serialized; private readonly byte[] _serialized;
public string Engine => Configuration.EngineAcBinary; public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
public string IoMode => Configuration.IoBufWrNew; public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrNew;
public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes => 0; public long SetupSerializeAllocBytes => 0;

View File

@ -47,9 +47,9 @@ internal sealed class AcBinaryInMemoryPipeBenchmark : ISerializerBenchmark, IDis
private bool _captureResult; private bool _captureResult;
private bool _disposed; private bool _disposed;
public string Engine => Configuration.EngineAcBinary; public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
public string IoMode => Configuration.IoInMemoryPipe; public BenchmarkIoMode IoMode => BenchmarkIoMode.InMemoryPipe;
public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes { get; } public long SetupSerializeAllocBytes { get; }

View File

@ -38,9 +38,9 @@ internal sealed class AcBinaryInMemoryRawByteArrayBenchmark : ISerializerBenchma
private bool _captureResult; private bool _captureResult;
private bool _disposed; private bool _disposed;
public string Engine => Configuration.EngineAcBinary; public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
public string IoMode => Configuration.IoInMemoryRaw; public BenchmarkIoMode IoMode => BenchmarkIoMode.InMemoryRaw;
public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes { get; } public long SetupSerializeAllocBytes { get; }

View File

@ -62,9 +62,9 @@ internal sealed class AcBinaryNamedPipeBenchmark : ISerializerBenchmark, IDispos
private bool _captureResult; // toggle: when true, ConsumeLoop stores result; otherwise discards private bool _captureResult; // toggle: when true, ConsumeLoop stores result; otherwise discards
private bool _disposed; private bool _disposed;
public string Engine => Configuration.EngineAcBinary; public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
public string IoMode => Configuration.IoNamedPipe; public BenchmarkIoMode IoMode => BenchmarkIoMode.NamedPipe;
public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes { get; } public long SetupSerializeAllocBytes { get; }

View File

@ -49,9 +49,9 @@ internal sealed class AcBinaryNamedPipeRawByteArrayBenchmark : ISerializerBenchm
private bool _captureResult; // toggle: when true, ConsumerLoop stores result; otherwise discards private bool _captureResult; // toggle: when true, ConsumerLoop stores result; otherwise discards
private bool _disposed; private bool _disposed;
public string Engine => Configuration.EngineAcBinary; public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
public string IoMode => Configuration.IoNamedPipeRaw; public BenchmarkIoMode IoMode => BenchmarkIoMode.NamedPipeRaw;
public string DispatchMode => _options.UseGeneratedCode ? Configuration.ModeSGen : Configuration.ModeRuntime; public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes { get; } public long SetupSerializeAllocBytes { get; }

View File

@ -0,0 +1,90 @@
namespace AyCode.Core.Serializers.Console.Benchmarks;
/// <summary>
/// Serializer engine identifier — replaces the prior <c>Configuration.EngineXxx</c> string constants
/// with a type-safe enum. The benchmark-result <c>Engine</c> column uses <see cref="ToDisplay"/> for
/// the human-readable form.
/// </summary>
internal enum BenchmarkEngine
{
AcBinary,
MemoryPack,
#if !AYCODE_NATIVEAOT
MessagePack,
#endif
SystemTextJson,
}
/// <summary>
/// I/O mode identifier — replaces the prior <c>Configuration.IoXxx</c> string constants. Note that
/// <see cref="NamedPipe"/> and <see cref="NamedPipeRaw"/> share the display string <c>"NamedPipe"</c>
/// (they distinguish chunked-framed vs raw-byte[] semantics, but render identically in the IO column);
/// the same applies to <see cref="InMemoryPipe"/> + <see cref="InMemoryRaw"/> (<c>"Pipe(in-mem)"</c>).
/// </summary>
internal enum BenchmarkIoMode
{
ByteArray,
BufWrReuse,
BufWrNew,
String,
NamedPipe,
NamedPipeRaw,
InMemoryPipe,
InMemoryRaw,
}
/// <summary>
/// Dispatch mode identifier — replaces the prior <c>Configuration.ModeXxx</c> string constants.
/// Describes how property access / type dispatch happens for a given benchmark row:
/// <list type="bullet">
/// <item><see cref="SGen"/> — compile-time source generator path (Unsafe.As&lt;T&gt; direct fields, slot-array wrapper lookup).</item>
/// <item><see cref="Runtime"/> — reflection / compiled-delegate path.</item>
/// <item><see cref="Hybrid"/> — SGen root with non-SGen child types reached via bridge methods (see docs/BINARY/BINARY_SGEN.md).</item>
/// </list>
/// </summary>
internal enum BenchmarkDispatchMode
{
SGen,
Runtime,
Hybrid,
}
/// <summary>
/// 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 <c>.log</c> file CSV/formatted output,
/// and the <c>.LLM</c> markdown table. Centralised here so every output formatter renders identically.
/// </summary>
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),
};
}

View File

@ -13,16 +13,16 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
/// </summary> /// </summary>
internal interface ISerializerBenchmark internal interface ISerializerBenchmark
{ {
/// <summary>Serializer engine — e.g. "AcBinary", "MemoryPack", "MessagePack".</summary> /// <summary>Serializer engine — typed enum, see <see cref="BenchmarkEnumExtensions.ToDisplay(BenchmarkEngine)"/> for the human-readable form.</summary>
string Engine { get; } BenchmarkEngine Engine { get; }
/// <summary>I/O mode — e.g. "Byte[]", "BufWr reuse", "BufWr new", "NamedPipe", "FileStream".</summary> /// <summary>I/O mode — typed enum, see <see cref="BenchmarkEnumExtensions.ToDisplay(BenchmarkIoMode)"/> for the human-readable form.</summary>
string IoMode { get; } BenchmarkIoMode 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> /// <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>
string DispatchMode { get; } BenchmarkDispatchMode DispatchMode { get; }
/// <summary>Options preset name — e.g. "FastMode", "Default", "NoIntern", "WithCompression".</summary> /// <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; } string OptionsPreset { get; }
/// <summary>Synthesized display name from Engine + IoMode + OptionsPreset.</summary> /// <summary>Synthesized display name from Engine + IoMode + OptionsPreset.</summary>
string Name => $"{Engine} ({IoMode}, {OptionsPreset})"; string Name => $"{Engine.ToDisplay()} ({IoMode.ToDisplay()}, {OptionsPreset})";
int SerializedSize { get; } int SerializedSize { get; }
string? OptionsDescription => null; 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> /// <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>

View File

@ -15,9 +15,9 @@ internal sealed class MemoryPackBenchmark : ISerializerBenchmark
private readonly MemoryPackSerializerOptions _options; private readonly MemoryPackSerializerOptions _options;
private readonly byte[] _serialized; private readonly byte[] _serialized;
public string Engine => Configuration.EngineMemoryPack; public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack;
public string IoMode => Configuration.IoByteArray; public BenchmarkIoMode IoMode => BenchmarkIoMode.ByteArray;
public string DispatchMode => Configuration.ModeSGen; // MemoryPack always uses [MemoryPackable] source-generated formatters public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes => 0; public long SetupSerializeAllocBytes => 0;

View File

@ -17,9 +17,9 @@ internal sealed class MemoryPackBufferWriterBenchmark : ISerializerBenchmark
private readonly byte[] _serialized; private readonly byte[] _serialized;
private readonly ArrayBufferWriter<byte> _bufferWriter; private readonly ArrayBufferWriter<byte> _bufferWriter;
public string Engine => Configuration.EngineMemoryPack; public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack;
public string IoMode => Configuration.IoBufWrReuse; public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrReuse;
public string DispatchMode => Configuration.ModeSGen; // MemoryPack always uses [MemoryPackable] source-generated formatters public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes { get; } public long SetupSerializeAllocBytes { get; }

View File

@ -15,9 +15,9 @@ internal sealed class MemoryPackFreshBufferWriterBenchmark : ISerializerBenchmar
private readonly MemoryPackSerializerOptions _options; private readonly MemoryPackSerializerOptions _options;
private readonly byte[] _serialized; private readonly byte[] _serialized;
public string Engine => Configuration.EngineMemoryPack; public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack;
public string IoMode => Configuration.IoBufWrNew; public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrNew;
public string DispatchMode => Configuration.ModeSGen; // MemoryPack always uses [MemoryPackable] source-generated formatters public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes => 0; public long SetupSerializeAllocBytes => 0;

View File

@ -18,9 +18,9 @@ internal sealed class MessagePackBenchmark : ISerializerBenchmark
private readonly MessagePackSerializerOptions _options; private readonly MessagePackSerializerOptions _options;
private readonly byte[] _serialized; private readonly byte[] _serialized;
public string Engine => Configuration.EngineMessagePack; public BenchmarkEngine Engine => BenchmarkEngine.MessagePack;
public string IoMode => Configuration.IoByteArray; public BenchmarkIoMode IoMode => BenchmarkIoMode.ByteArray;
public string DispatchMode => Configuration.ModeSGen; // MessagePack uses [MessagePackObject] source-generated formatters (StandardResolver) public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MessagePack uses [MessagePackObject] source-generated formatters (StandardResolver)
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serialized.Length; public int SerializedSize => _serialized.Length;
public long SetupSerializeAllocBytes => 0; public long SetupSerializeAllocBytes => 0;

View File

@ -17,9 +17,9 @@ internal sealed class SystemTextJsonBenchmark : ISerializerBenchmark
private readonly string _serialized; private readonly string _serialized;
private readonly byte[] _serializedUtf8; private readonly byte[] _serializedUtf8;
public string Engine => Configuration.EngineSystemTextJson; public BenchmarkEngine Engine => BenchmarkEngine.SystemTextJson;
public string IoMode => Configuration.IoString; public BenchmarkIoMode IoMode => BenchmarkIoMode.String;
public string DispatchMode => Configuration.ModeRuntime; // System.Text.Json default uses reflection-based metadata (no source generator opt-in here) public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.Runtime; // System.Text.Json default uses reflection-based metadata (no source generator opt-in here)
public string OptionsPreset { get; } public string OptionsPreset { get; }
public int SerializedSize => _serializedUtf8.Length; public int SerializedSize => _serializedUtf8.Length;
public long SetupSerializeAllocBytes => 0; public long SetupSerializeAllocBytes => 0;

View File

@ -41,24 +41,7 @@ internal static class Configuration
// 1 = Compact, 2 = Fast // 1 = Compact, 2 = Fast
internal static WireMode SelectedWireMode = WireMode.Compact; internal static WireMode SelectedWireMode = WireMode.Compact;
// Serializer name constants // Engine / IO mode / Dispatch mode identifiers → Benchmarks/BenchmarkEnums.cs (typed enums with ToDisplay)
// 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)";
// Single source of truth for the chunk size used by ALL pipe-related benchmarks (NamedPipe PipeChunk, // 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. // 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. // overrides across individual benchmark rows.
internal const int PipeChunkSize = 4096; 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<T> 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 // 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 // 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 // per-sample window across cells of vastly different per-op cost (Small ~6 ns/op vs Large

View File

@ -1,4 +1,5 @@
using AyCode.Core.Serializers.Binaries; using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Serializers.Console.Benchmarks;
using AyCode.Core.Tests.TestModels; using AyCode.Core.Tests.TestModels;
using System.Globalization; using System.Globalization;
using System.Runtime.CompilerServices; 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. // 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 testResults = results.Where(r => r.TestDataName == testData.DisplayName).OrderBy(r => RtPerOp(r)).ToList();
// Baseline switched MessagePack → MemoryPack: MemoryPack is the SOTA performance leader. // 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). // 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. // 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, '─') + "┐"); 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). // 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. // 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. // 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) var isHighlighted = (result.Engine == BenchmarkEngine.MemoryPack && result.IoMode == BenchmarkIoMode.ByteArray)
|| (result.Engine == Configuration.EngineAcBinary && result.IoMode == Configuration.IoByteArray && result.DispatchMode == Configuration.ModeSGen); || (result.Engine == BenchmarkEngine.AcBinary && result.IoMode == BenchmarkIoMode.ByteArray && result.DispatchMode == BenchmarkDispatchMode.SGen);
var prefix = isHighlighted ? "│►" : "│ "; var prefix = isHighlighted ? "│►" : "│ ";
var suffix = isHighlighted ? "◄│" : " │"; var suffix = isHighlighted ? "◄│" : " │";
@ -224,7 +225,7 @@ internal static class Output
// Color logic: Green = winner (faster), Red = loser (slower) // Color logic: Green = winner (faster), Red = loser (slower)
if (isHighlighted && memPackResult != null && acBinaryResult != null) 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); var memPackFaster = RtPerOp(memPackResult) < RtPerOp(acBinaryResult);
if (isMemPack) 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) 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"); System.Console.WriteLine($"{"Fastest Round-trip",-20} │ {fastestRt.Name,-40} │ {fastestRt.AvgPerOp,12:F2} µs/op");
// Overall AcBinary (SGen) vs MemoryPack comparison. // Overall AcBinary (SGen) vs MemoryPack comparison.
var memPackSerResults = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.SerializeTimeMs > 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 == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.DeserializeTimeMs > 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 == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.RoundTripTimeMs > 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 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 == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.DeserializeTimeMs > 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 == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.RoundTripTimeMs > 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 // Skip comparison if no data available
if (memPackRtResults.Count == 0 || acBinaryRtResults.Count == 0) 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. // 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 sizeAcResults = results.Where(r => (r.Engine == BenchmarkEngine.AcBinary && r.IoMode == BenchmarkIoMode.ByteArray && r.DispatchMode == BenchmarkDispatchMode.SGen)).ToList();
var sizeMpResults = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray)).ToList(); var sizeMpResults = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray)).ToList();
var serStats = ComputeOverallStats(acBinarySerResults, memPackSerResults, SerPerOp); var serStats = ComputeOverallStats(acBinarySerResults, memPackSerResults, SerPerOp);
var desStats = ComputeOverallStats(acBinaryDesResults, memPackDesResults, DesPerOp); var desStats = ComputeOverallStats(acBinaryDesResults, memPackDesResults, DesPerOp);
@ -472,7 +473,7 @@ internal static class Output
var testResults = results.Where(r => r.TestDataName == testData.DisplayName).ToList(); var testResults = results.Where(r => r.TestDataName == testData.DisplayName).ToList();
foreach (var result in testResults) 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(); 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. // 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 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 memPackResult = testResults.FirstOrDefault(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray));
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));
sb.AppendLine(); sb.AppendLine();
sb.AppendLine($"--- {testData.DisplayName} ---"); sb.AppendLine($"--- {testData.DisplayName} ---");
@ -497,7 +498,7 @@ internal static class Output
var rank = 1; var rank = 1;
foreach (var result in testResults) 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 prefix = isHighlighted ? "► " : " ";
var size = $"{result.SerializedSize:N0}"; var size = $"{result.SerializedSize:N0}";
@ -527,13 +528,13 @@ internal static class Output
sb.AppendLine(); sb.AppendLine();
sb.AppendLine("=== AcBinary (Byte[], SGen) vs MemoryPack (Byte[]) (Overall) ==="); 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 memPackSerResults2 = results.Where(r => (r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray) && r.SerializeTimeMs > 0).ToList();
var memPackDesResults2 = results.Where(r => (r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.DeserializeTimeMs > 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 == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray) && r.RoundTripTimeMs > 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 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 == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.DeserializeTimeMs > 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 == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen) && r.RoundTripTimeMs > 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 // Skip comparison block if either side has no Byte[] data
if (memPackRtResults2.Count == 0 || acBinaryRtResults2.Count == 0) if (memPackRtResults2.Count == 0 || acBinaryRtResults2.Count == 0)
@ -547,8 +548,8 @@ internal static class Output
return; return;
} }
var sizeAcResults2 = results.Where(r => (r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen)).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 == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray)).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, "Serialize", "µs/op", ComputeOverallStats(acBinarySerResults2, memPackSerResults2, SerPerOp));
AppendOverallLine(sb, "Ser Alloc", "B/op", ComputeOverallStats(acBinarySerResults2, memPackSerResults2, r => r.SerializeAllocBytesPerOp), "F0"); 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 var iterCol = r.IsRoundTripOnly
? r.RoundTripIterations.ToString(inv) ? r.RoundTripIterations.ToString(inv)
: $"{(r.SerializeIterations > 0 ? r.SerializeIterations.ToString(inv) : "-")} / {(r.DeserializeIterations > 0 ? r.DeserializeIterations.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 // Overall AcBinary (SGen, Byte[]) vs MemoryPack (Byte[]) comparison
var memPackByteArrayResults = results.Where(r => r.Engine == Configuration.EngineMemoryPack && r.IoMode == Configuration.IoByteArray).ToList(); var memPackByteArrayResults = results.Where(r => r.Engine == BenchmarkEngine.MemoryPack && r.IoMode == BenchmarkIoMode.ByteArray).ToList();
var acBinarySGenByteArrayResults = results.Where(r => r.Engine == Configuration.EngineAcBinary && r.IoMode == Configuration.IoByteArray && r.DispatchMode == Configuration.ModeSGen).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 memPackSerResultsLlm = memPackByteArrayResults.Where(r => r.SerializeTimeMs > 0).ToList();
var memPackDesResultsLlm = memPackByteArrayResults.Where(r => r.DeserializeTimeMs > 0).ToList(); var memPackDesResultsLlm = memPackByteArrayResults.Where(r => r.DeserializeTimeMs > 0).ToList();
var memPackRtResultsLlm = memPackByteArrayResults.Where(r => r.RoundTripTimeMs > 0).ToList(); var memPackRtResultsLlm = memPackByteArrayResults.Where(r => r.RoundTripTimeMs > 0).ToList();