# AyCode.Core.Serializers.Console

Interactive console runner for the serializer benchmark suite. Targets .NET 9.

> **Companion**: shares its workload + reporting infrastructure with the BDN runner in [`AyCode.Benchmark/`](../AyCode.Benchmark/README.md) via `<ProjectReference>`. See that project's README for the full dual-runner architecture.

## Role

This is the **fast-iteration** half of the benchmark stack — a custom adaptive measure engine optimized for short turnaround (~1-3 min full run) during micro-optimization loops. The BDN half lives in `AyCode.Benchmark` and produces statistically tighter numbers (~5-15 min full run) for before-commit validation. Both runners emit the **same** `.log` / `.LLM` / `.output` triplet to `Test_Benchmark_Results/Benchmark/` — Console prefixes with `Console.`, BDN with `Bdn.`.

## Compared serializers

- **AcBinary** — multiple options presets: `FastMode` (Compact wire, no ref handling, no interning), `Default` (with ref handling + interning), plus SGen / Runtime dispatch variants and Compact / Fast wire modes.
- **MemoryPack** — SOTA baseline, wire-mode-aligned with AcBinary for apples-to-apples encoding comparison (UTF-8 ↔ Compact, UTF-16 ↔ Fast).
- **MessagePack** — JIT-only (AOT incompatible due to dynamic resolver).
- **System.Text.Json** — reference comparison (commented out in `CreateSerializers` by default).

## Key files

- [`Program.cs`](Program.cs) — entry point. Parses CLI args (`Core` / `Comprehensive` / `Edge` / per-cell / op-mode / serializer-set) or falls into interactive `Menu`.
- [`Menu.cs`](Menu.cs) — interactive layer/serializer-set selection + nested settings (iteration counts, wire mode, charset).
- [`BenchmarkLoop.cs`](BenchmarkLoop.cs) — custom adaptive measure engine. CPU 0 affinity pin + High priority for stabilization, JIT pre-warmup, phase-isolated Ser/Des warmup→measure with `GC.Collect` at every boundary, 10-sample median + pilot discard, adaptive iter calibration to ~250ms/cell wall-clock, dedicated allocation-only sample.
- [`Configuration.cs`](Configuration.cs) — Console-side state (`SelectedWireMode`, `WarmupIterations`, `BenchmarkSamples`, `TargetSampleMs`, charset selection, `BuildConfiguration` const from `#if DEBUG/RELEASE/AYCODE_NATIVEAOT`).

Workload + reporting types — `ISerializerBenchmark`, `BenchmarkResult`, `BenchmarkOptions`, `BenchmarkEnums`, `BenchmarkReportWriter`, `ReportingContext`, the 12 concrete `*Benchmark<T>` classes (`AcBinaryBenchmark`, `MemoryPackBenchmark`, `AcBinaryBufferWriterBenchmark`, ...), `RoundTripValidator` — live in [`AyCode.Benchmark/Workloads/Scenarios/`](../AyCode.Benchmark/Workloads/Scenarios/) and [`AyCode.Benchmark/Reporting/`](../AyCode.Benchmark/Reporting/).

## Test data

5 cells, provided by `AyCode.Core.Tests.TestModels.BenchmarkTestDataProvider*`:

- **Small** (2×2×2×2)
- **Medium** (3×3×3×4)
- **Large** (5×5×5×10)
- **Repeated Strings** (10 items, string-deduplication stress)
- **Deep Nested** (2×4×4×8, depth stress)

20% IId reference rate by default. Two graph variants (`TestOrder_All_False` / `_All_True`) are built per cell — AcBinary's option preset picks which variant gets fed to it (`UsesAllFalseVariant` rule in `BenchmarkLoop`).

## Charset profiles (Menu → Settings → Charset)

Controls the `BenchmarkTestDataProvider.LongStringSuffix` — the string-tail appended to property values. Influences string-marker selection on the wire (FixStrAscii vs StringSmall / Medium / Big / StringAscii), interning hit rates, and UTF-8 encode cost.

**Consistent length across all charsets** (UTF-16 char count): every `*Short` = 40 char, every `*Long` = 280 char (= Short × 7). Isolates the workload variable to UTF-8 byte content per charset (1-byte ASCII vs 2-byte Latin1 / Cyrillic vs 3-byte CJK vs mixed) — wire-size and encode/decode cost differences are pure charset effects, not length effects.

| Profile | UTF-16 char | UTF-8 byte (approx) | Tier |
|---|---|---|---|
| `Latin1FixAscii` | 0 | 0 | FixStrAscii / FixStr-equivalent (baseline-only) |
| `AsciiShort` | 40 | 40 | StringAscii (167) |
| `AsciiLong` | 280 | 280 | StringAscii (167) |
| `Latin1Short` | 40 | ~72 | StringSmall (91) |
| `Latin1Long` (**default**) | 280 | ~504 | StringMedium (94) |
| `CjkBmpShort` | 40 | ~104 | StringSmall |
| `CjkBmpLong` | 280 | ~728 | StringMedium |
| `CyrillicShort` | 40 | ~72 | StringSmall |
| `CyrillicLong` | 280 | ~504 | StringMedium |
| `MixedShort` | 40 | ~88 | StringSmall |
| `MixedLong` | 280 | ~616 | StringMedium |

## CLI

```
dotnet run -c Release --project AyCode.Core.Serializers.Console -- [arg]
```

| Arg | Result |
|---|---|
| _(no args)_ | Interactive menu — pick layer (Core / Comprehensive / Edge / Small / Medium / Large / Repeated / Deep / All) × serializer-set (Standard / FastestByte ["F"] / AsyncPipe ["P"]). |
| `Core` / `Comprehensive` / `Edge` / `Small` / `Medium` / `Large` / `Repeated` / `Deep` / `All` | Run that layer at `Standard` serializer-set, `All` op-mode. |
| `FastestByte` / `AsyncPipe` / `Standard` | Run that serializer-set, `All` layer, `All` op-mode. |
| `Serialize` / `Deserialize` / `All` | Run that op-mode, `All` layer, `Standard` serializer-set. |
| `quick` | Single-sample fast mode (Debug-equivalent — very loose numbers, smoke-test only). |

Output: `Test_Benchmark_Results/Benchmark/Console.FullBenchmark_<Build>_<timestamp>.{log,LLM,output}`.

## Dependencies

| Dependency | Purpose |
|---|---|
| `AyCode.Core` (ProjectReference) | AcBinary serializer |
| `AyCode.Core.Tests` (ProjectReference) | Test data factory + test models |
| `AyCode.Benchmark` (ProjectReference) | Shared workload + reporting (`ISerializerBenchmark`, `BenchmarkResult`, `BenchmarkReportWriter`, `ReportingContext`, the 12 concrete benchmark classes) |
| `MemoryPack` | Comparison target (also via Workloads) |
| `MessagePack` | Comparison target |
| `Newtonsoft.Json` | Comparison target (currently disabled) |

## Build & publish notes

- `<StartupObject>AyCode.Core.Serializers.Console.Program</StartupObject>` in the csproj explicitly disambiguates the entry point — necessary because this Exe references another Exe (`AyCode.Benchmark`), and the build would otherwise complain about multiple `Main` methods.
- AOT publish (`dotnet publish -c Release`) is configured via `'$(_IsPublishing)' == 'true'` PropertyGroup. The Benchmark project's BDN-stack (BenchmarkDotNet, Iced disassembler, MongoDB.Bson) is pulled in transitively — accepted tradeoff for the unified workload sharing.
