AyCode.Core/AyCode.Benchmark/AcBinaryVsMemPackBenchmark.cs

87 lines
4.5 KiB
C#

using AyCode.Core.Benchmarks.Workloads.Scenarios;
using AyCode.Core.Serializers;
using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Tests.TestModels;
using BenchmarkDotNet.Attributes;
namespace AyCode.Core.Benchmarks;
/// <summary>
/// BDN benchmark mirroring the Console app's "F" menu (<c>SerializerSelectionMode.FastestByte</c>) —
/// the focused 1:1 comparison between <b>AcBinary FastMode Byte[]</b> and <b>MemoryPack Default Byte[]</b>
/// across the 5 production-shaped test data cells (Small / Medium / Large / Repeated / Deep).
///
/// <para>Why this exists: the Console app's adaptive measurement engine gives fast turnaround but is
/// noise-prone; BDN's warmup + iteration + outlier-removal stack tightens the inter-engine delta to
/// the point where ~1-2% micro-optimizations become detectable. Both runners feed the SAME
/// <see cref="ISerializerBenchmark"/>-implementing workload (<see cref="AcBinaryBenchmark{T}"/> /
/// <see cref="MemoryPackBenchmark{T}"/>) — so the BDN numbers are directly comparable to Console's
/// <c>Console.FullBenchmark_Release_*.LLM</c> rows, only with tighter confidence intervals.</para>
///
/// <para>Output: BDN writes its native artifacts to <c>Test_Benchmark_Results/Benchmark/BDN/</c> (set
/// globally in <c>Program.cs</c> via <c>WithArtifactsPath</c>). <see cref="BdnSummaryAdapter"/> then
/// translates the <see cref="BenchmarkDotNet.Reports.Summary"/> into <see cref="Reporting.BenchmarkResult"/>
/// rows and emits the unified <c>Bdn.FullBenchmark_*.{log,LLM,output}</c> triplet next to Console's
/// counterparts in <c>Test_Benchmark_Results/Benchmark/</c>.</para>
/// </summary>
[MemoryDiagnoser]
public class AcBinaryVsMemPackBenchmark
{
/// <summary>
/// The 5 TestData cells matching Console's <c>BenchmarkLayer.Core</c> set —
/// Small (2x2x2x2) / Medium (3x3x3x4) / Large (5x5x5x10) / Repeated (10 items) / Deep (2x4x4x8).
/// Resolved at <see cref="GlobalSetup"/> time via <see cref="BenchmarkTestDataProvider_All_False.CreateTestDataSets"/>
/// (same provider Console uses) so the workload graphs are bit-for-bit identical.
/// </summary>
public static IEnumerable<string> TestDataNames => new[] { "Small", "Medium", "Large", "Repeated", "Deep" };
[ParamsSource(nameof(TestDataNames))]
public string TestData { get; set; } = "";
/// <summary>
/// Engine axis: AcBinary FastMode + Compact wire (UTF-8) vs MemoryPack Default (UTF-8). Compact-on-both-sides
/// keeps the string-encoding dimension constant so the comparison reflects engine differences only.
/// </summary>
[Params("AcBinary", "MemoryPack")]
public string Engine { get; set; } = "";
private ISerializerBenchmark _serializer = null!;
[GlobalSetup]
public void Setup()
{
// BDN runs each benchmark in an isolated child process — the parent's charset selection (a static
// field) does NOT cross the process boundary, so the child would otherwise fall back to the
// compile-time default (Latin1Long). Pin the BDN serializer benchmark to Latin1Short here so its
// cells line up with the Console Latin1Short runs. (Mirrored in BdnSummaryAdapter.WriteResults
// for the parent process — .LLM charset label + Size(B) column.)
BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.Latin1Short;
var allTestData = BenchmarkTestDataProvider_All_False.CreateTestDataSets();
var testDataSet = (TestDataSet<TestOrder_All_False>)allTestData.First(t => t.Name.StartsWith(TestData));
if (Engine == "AcBinary")
{
var options = AcBinarySerializerOptions.FastMode;
options.WireMode = WireMode.Compact;
_serializer = new AcBinaryBenchmark<TestOrder_All_False>(testDataSet.Order, options, "FastMode");
}
else
{
// MemoryPack's wire-mode-aligned ctor — Compact ↔ UTF-8 default for apples-to-apples vs AcBinary Compact.
_serializer = new MemoryPackBenchmark<TestOrder_All_False>(testDataSet.Order, WireMode.Compact, "Default");
}
// Round-trip correctness check before the BDN harness starts measuring — same gate the Console
// runner enforces. Fails the run early if anything's broken (rather than producing meaningless numbers).
if (!_serializer.VerifyRoundTrip())
throw new InvalidOperationException($"Round-trip verification FAILED for {Engine} on {TestData}.");
}
[Benchmark]
public void Serialize() => _serializer.Serialize();
[Benchmark]
public void Deserialize() => _serializer.Deserialize();
}