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; /// /// BDN benchmark mirroring the Console app's "F" menu (SerializerSelectionMode.FastestByte) — /// the focused 1:1 comparison between AcBinary FastMode Byte[] and MemoryPack Default Byte[] /// across the 5 production-shaped test data cells (Small / Medium / Large / Repeated / Deep). /// /// 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 /// -implementing workload ( / /// ) — so the BDN numbers are directly comparable to Console's /// Console.FullBenchmark_Release_*.LLM rows, only with tighter confidence intervals. /// /// Output: BDN writes its native artifacts to Test_Benchmark_Results/Benchmark/BDN/ (set /// globally in Program.cs via WithArtifactsPath). then /// translates the into /// rows and emits the unified Bdn.FullBenchmark_*.{log,LLM,output} triplet next to Console's /// counterparts in Test_Benchmark_Results/Benchmark/. /// [MemoryDiagnoser] public class AcBinaryVsMemPackBenchmark { /// /// The 5 TestData cells matching Console's BenchmarkLayer.Core set — /// Small (2x2x2x2) / Medium (3x3x3x4) / Large (5x5x5x10) / Repeated (10 items) / Deep (2x4x4x8). /// Resolved at time via /// (same provider Console uses) so the workload graphs are bit-for-bit identical. /// public static IEnumerable TestDataNames => new[] { "Small", "Medium", "Large", "Repeated", "Deep" }; [ParamsSource(nameof(TestDataNames))] public string TestData { get; set; } = ""; /// /// 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. /// [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)allTestData.First(t => t.Name.StartsWith(TestData)); if (Engine == "AcBinary") { var options = AcBinarySerializerOptions.FastMode; options.WireMode = WireMode.Compact; _serializer = new AcBinaryBenchmark(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(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(); }