# AyCode.Benchmark BenchmarkDotNet performance suite **plus** the shared workload / reporting infrastructure used by both BDN and the Console runner. Targets .NET 9. ## Role: dual-purpose project This project plays **two roles**: 1. **BDN runner Exe** — standalone benchmark host (`Program.cs` + `[Benchmark]`-decorated classes). Invoke via `dotnet run -c Release --project AyCode.Benchmark -- `. 2. **Shared workload + reporting library** — exposes `public` types under [`Workloads/Scenarios/`](Workloads/Scenarios/) and [`Reporting/`](Reporting/) that [`AyCode.Core.Serializers.Console`](../AyCode.Core.Serializers.Console/README.md) consumes via ``. Both runners feed the SAME `ISerializerBenchmark` workload (same test data graphs, same wire options, same payload sizes) — so Console's adaptive-engine numbers and BDN's iteration-based numbers are **directly comparable**. ## Output convention Both runners emit a unified `.log` / `.LLM` / `.output` triplet to `Test_Benchmark_Results/Benchmark/` (resolved at runtime via walk-up to the nearest `AyCode.Core.sln` — worktree-aware): | File | Source | Content | |---|---|---| | `Console.FullBenchmark__.log` | Console runner | Human-readable formatted view | | `Console.FullBenchmark__.LLM` | Console runner | Markdown table, LLM-paste-friendly | | `Console.FullBenchmark__.output` | Console runner | Hex dump of Large cell binary | | `Bdn.FullBenchmark__.log` | BDN runner | Same format as Console | | `Bdn.FullBenchmark__.LLM` | BDN runner | Same | | `Bdn.FullBenchmark__.output` | BDN runner | Same | BDN-native artifacts (BDN's own reports, raw measurements, run logs) go to `Test_Benchmark_Results/Benchmark/BDN/` — kept separate so the unified Console+BDN `.log/.LLM/.output` triplet stays uncluttered. ## Architecture ``` ┌────────────────────────────────────────────────────────────────┐ │ AyCode.Benchmark (this project) │ │ │ │ Workloads/Scenarios/ public — shared workload types │ │ ISerializerBenchmark, BenchmarkOptions, BenchmarkEnums, │ │ AcBinaryBenchmark, MemoryPackBenchmark, │ │ AcBinaryBufferWriterBenchmark, ... (12 concretes), │ │ RoundTripValidator │ │ │ │ Reporting/ public — shared reporting types │ │ BenchmarkResult, ReportingContext, BenchmarkReportWriter │ │ │ │ AcBinaryVsMemPackBenchmark.cs BDN [Benchmark] class │ │ (mirrors Console "F" menu) │ │ BdnSummaryAdapter.cs Summary → BenchmarkResult → │ │ BenchmarkReportWriter.SaveAll │ │ Program.cs BDN entry + CLI dispatch │ │ │ │ + KEEP: JitDisassemblyBenchmark, RefForeachBenchmark, │ │ TaskHelperBenchmarks, ValueTypePassingBenchmark, │ │ SourceGeneratorBenchmarks, │ │ SignalRCommunicationBenchmarks, │ │ SignalRRoundTripBenchmarks │ └────────────────────────────────────────────────────────────────┘ ▲ │ ProjectReference (one-way) │ ┌────────────────────────────────────────────────────────────────┐ │ AyCode.Core.Serializers.Console │ │ │ │ BenchmarkLoop.cs custom adaptive measure engine │ │ (CPU 0 pin, High priority, phase-isolated warmup, │ │ 10-sample median + pilot, ~250ms/cell calibration) │ │ Menu.cs / Configuration.cs / Program.cs Console UX │ │ │ │ Uses Benchmark's: │ │ - Workloads/Scenarios/* (interface + concrete benchmarks) │ │ - Reporting/BenchmarkReportWriter (SaveAll, Print...) │ └────────────────────────────────────────────────────────────────┘ ``` ## Two runners — same workload, different measurement engines | Aspect | Console (custom engine) | BDN | |---|---|---| | Use case | Fast iteration during micro-opt loops | Statistically confident before-commit validation | | Measurement | Adaptive per-cell iter (target ~250ms), 10 samples + pilot, median | Warmup + N iterations, outlier removal, JIT-stabilized, process-spawn isolation | | Time per full run | ~1-3 min | ~5-15 min | | Noise floor | ~3-5% inter-engine delta visible | ~1-2% | | Output format | Identical (same `BenchmarkReportWriter` writes both) | | The Console and BDN outputs use the SAME `BenchmarkResult` DTO and the SAME formatter, so cells are directly comparable: pick a cell in `Console.FullBenchmark_*.LLM`, find the same cell in `Bdn.FullBenchmark_*.LLM` — deltas should agree within BDN's tighter CI. ## CLI ``` dotnet run -c Release --project AyCode.Benchmark -- ``` | Switch | Description | |---|---| | `--serializers` | AcBinary FastMode Byte[] vs MemoryPack Default Byte[] across 5 TestData cells (mirrors Console "F" menu / FastestByte). Emits `Bdn.FullBenchmark_*.{log,LLM,output}` + BDN-native artifacts under `BDN/`. | | `--jitasm` | JIT disassembly analysis (x64 asm of serialize/deserialize hot path). | | `--quick` | Quick inline benchmark (custom Stopwatch-based, not BDN). | | `--test` / `--testmsgpack` | Quick smoke tests. | | `--save-coverage ` | Save coverage file into `Test_Benchmark_Results/CoverageReport/`. | | _(no args)_ | Interactive `BenchmarkSwitcher` — pick from all `[Benchmark]` classes in the assembly. | ## Key files ### Serializer benchmark stack (the refactor scope) - [`AcBinaryVsMemPackBenchmark.cs`](AcBinaryVsMemPackBenchmark.cs) — BDN `[MemoryDiagnoser]` class. `[ParamsSource]`(TestData = Small/Medium/Large/Repeated/Deep) × `[Params]`(Engine = AcBinary/MemoryPack). `[GlobalSetup]` hidrátálja a Workloads scenario-ját + round-trip-verify. - [`BdnSummaryAdapter.cs`](BdnSummaryAdapter.cs) — `Summary → List` translator (groups per `(TestData × Engine)`, ns → ms conversion, GcStats → allocated-bytes-per-op). Calls `BenchmarkReportWriter.PrintGroupedResults` + `SaveAll(ctx with SourceTag="Bdn", ...)`. - [`Program.cs`](Program.cs) — BDN entry. Sets global `WithArtifactsPath(.../Benchmark/BDN)`; `--serializers` switch wires `BenchmarkRunner.Run` + adapter. - [`Workloads/Scenarios/`](Workloads/Scenarios/) — shared workload types (see folder README). - [`Reporting/`](Reporting/) — shared reporting types (see folder README). ### KEEP benchmarks (independent — not in the serializer-refactor scope) - [`JitDisassemblyBenchmark.cs`](JitDisassemblyBenchmark.cs) — JIT analysis: emits `.asm` files for serialize/deserialize hot paths. - [`TaskHelperBenchmarks.cs`](TaskHelperBenchmarks.cs) — Task/timing utilities (WaitToAsync, custom ThreadPool, UtcNow.Ticks vs TickCount64). - [`ValueTypePassingBenchmark.cs`](ValueTypePassingBenchmark.cs) — Copy-by-value vs `in` parameter for 16-byte types. - [`RefForeachBenchmark.cs`](RefForeachBenchmark.cs) — Collection iteration patterns (array vs list, foreach vs index, ref readonly). - [`SourceGeneratorBenchmarks.cs`](SourceGeneratorBenchmarks.cs) — Source-generated vs runtime reflection serializers (PureContractlessBenchmark, RepeatedStringBenchmark). - [`SignalRCommunicationBenchmarks.cs`](SignalRCommunicationBenchmarks.cs) — Full-stack SignalR perf (client → server → response → round-trip). - [`SignalRRoundTripBenchmarks.cs`](SignalRRoundTripBenchmarks.cs) — SignalR primitives/complex/collections benchmarks. ## Dependencies | Dependency | Purpose | |---|---| | `BenchmarkDotNet` | BDN harness | | `MemoryPack` | Comparison target (used by Workloads scenarios + BDN class) | | `MessagePack` | Comparison target (KEEP benchmarks + Workloads MessagePackBenchmark scenario) | | `MongoDB.Bson` | KEEP-side comparison target | | `Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers` | VS Profiler integration | | `AyCode.Core` (ProjectReference) | AcBinary serializer | | `AyCode.Core.Tests` (ProjectReference) | Test data factory (`TestDataFactory`, `TestOrder_All_False/True`, `BenchmarkTestDataProvider*`) | | `AyCode.Core.Serializers.SourceGenerator` (Analyzer-only) | SGen for `[AcBinarySerializable]`-tagged types |