# Сборка и benchmark-методика

## Профили кода

Все сравниваемые бинарники — Release x64 `/O2`, `/MT`, без IPO/LTO. Отличается
только ось SIMD:

| Профиль | Ключи проекта | Назначение |
|---|---|---|
| `scalar` | `/O2 /d2Qvec- /Qvec-report:1 /Oi-` | MSVC-specific `project-novec` |
| `baseline` | `/O2`, без `/arch:AVX*` | обычный MSVC x64 |
| `avx2` | `/O2 /arch:AVX2` | разрешён AVX2 |

`/d2Qvec-` — внутренний, недокументированный ключ MSVC, а не переносимая
гарантия. Командная строка проверена автоматическим аудитом для MSVC 19.51;
вывод `/Qvec-report` пока не разбирается как отдельное доказательство. `scalar`
не означает «ни одной SIMD-инструкции в процессе»: x64 ABI имеет SSE2-baseline,
а CRT `memmove` может runtime-dispatch-ить SIMD. Дополнительно задаётся
`_USE_STD_VECTOR_ALGORITHMS=0`, чтобы STL не включала свои явно
векторизованные алгоритмы. Это честнее называть `project-novec`.

## Матрица

Benchmark сравнивает:

- reserved `std::vector`;
- `std::deque`;
- `std::list` с index semantics;
- forced tiered для каждого leaf 64/128/256/512/1024;
- adaptive deferred;
- experimental adaptive eager-read.

`std::deque` и `std::list` — только внешние фиксированные baselines;
`AdaptiveSequence` никогда в них не переключается. Для `std::list` runner
эмулирует index semantics линейным проходом, поэтому доступ по индексу имеет
O(N).

Типы: `uint32_t`, `uint64_t`, trivially-copyable 16/32/64-byte значения и
non-trivial movable string wrapper. Workloads включают random access, traversal,
append, 99.99/99/80/50% reads, uniform/localized edits, bursty edits и отдельный
phase trace:

```text
uniform edits -> maintenance
localized edits -> maintenance
reads -> maintenance
```

Последний trace нужен именно для проверки смены leaf, а не только
`vector↔tiered`. Время rebuild остаётся внутри измеряемого end-to-end интервала.

Tuning перебирает leaf 64…1024 и maximum directory levels 2/3/4. Каталог сам
останавливает построение на минимально достаточной глубине.

## CSV

Raw CSV содержит profile/type/workload/N/seed, initial и final tier config,
total/ns-per-op, p50/p95/p99/max edit latency, checksum, allocated bytes,
storage switches, shape rebuilds и final mode. Checksum должен совпасть у всех
контейнеров одной трассы.

`tools/analyze_results.ps1` сначала берёт median повторов, затем сравнивает с
лучшим фиксированным baseline в каждой ячейке. Итоговый geometric mean считается
иерархически по sizes → workloads → types → profiles, чтобы семейство с большим
числом строк не получило лишний вес.

## Масштабы запуска

```bat
benchmark.bat smoke   rem проверка всего конвейера
benchmark.bat quick   rem рабочее исследование
benchmark.bat full    rem финальные повторы, длительный запуск
```

Отдельная large-матрица с обязательным reserved vector, fixed leaf 64…1024 и
adaptive запускается внешним process pool. Второй аргумент — число workers:

```bat
large_benchmark.bat smoke 12
large_benchmark.bat full 12 3
large_benchmark.bat full 12 3 huge
```

Каждый worker закрепляется за отдельным logical CPU. Третий аргумент ограничивает
число одновременно работающих N=100M-ячеек и лишь снижает риск pagefile; это
эвристика, а не измеритель доступной памяти. Четвёртый аргумент `huge` оставляет
только N=100M. Large raw,
per-cell partial/log, план, environment и сводки сохраняются в отдельной папке
`results/benchmarks/`.

`large_benchmark.bat full 12 3 huge` воспроизводит только N=100M с удлинённым
512-edit horizon и сам проверяет поддержку AVX2. Прямой PowerShell runner такой
CPU-проверки не выполняет: при его ручном запуске `avx2` надо исключить на
неподдерживаемой машине.

Для публикации финального числа нужны одинаковый power plan, отсутствие тяжёлой
фоновой нагрузки, закрепление на физическом ядре, randomized paired order и
holdout workloads/seeds. Текущие quick-данные являются calibration set, а не
финальным доказательством.
