AyCode.Core/AyCode.Benchmark/README.md

130 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 -- <switch>`.
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 `<ProjectReference>`.
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_<Build>_<ts>.log` | Console runner | Human-readable formatted view |
| `Console.FullBenchmark_<Build>_<ts>.LLM` | Console runner | Markdown table, LLM-paste-friendly |
| `Console.FullBenchmark_<Build>_<ts>.output` | Console runner | Hex dump of Large cell binary |
| `Bdn.FullBenchmark_<Build>_<ts>.log` | BDN runner | Same format as Console |
| `Bdn.FullBenchmark_<Build>_<ts>.LLM` | BDN runner | Same |
| `Bdn.FullBenchmark_<Build>_<ts>.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<T>, MemoryPackBenchmark<T>, │
│ AcBinaryBufferWriterBenchmark<T>, ... (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>
```
| 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 <file>` | 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<BenchmarkResult>` 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<AcBinaryVsMemPackBenchmark>` + 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 |