15 KiB
Quick-checkpoint 20260811-213422
Это итоговый engineering quick-checkpoint текущей итерации. Он фиксирует согласованную трёхпрофильную сборку, benchmark-матрицу и проверку адаптации, но не является full-size независимым holdout и не доказывает универсальное превосходство контейнера.
Происхождение серии
- v14 (
20260811-hysteresis-smoke-v14) проверил завершённый hysteresis на baseline smoke. - v15 (
20260811-policy-quick-v15-hysteresis) проверил policy на baseline quick;moderate deferredстал лучшим из адаптивных вариантов этой серии. - этот run повторил полную quick-матрицу для
scalar,baselineиavx2и является основной точкой текущего handoff.
Среда и профили сборки
Среда сохранена в environment.json и
environment.txt: AMD Ryzen 9 5900X, Windows 10 Pro 25H2,
MSVC 19.51.36248.0 x64, CMake 4.3.1-msvc1, план питания Balanced. Во всех Release
целях использованы /O2, статический runtime /MT, без IPO/LTO (/GL не
использовался).
| Профиль | Существенные параметры |
|---|---|
scalar |
/O2 /d2Qvec- /Qvec-report:1 /Oi-, без /arch:AVX* |
baseline |
/O2, автовекторизация разрешена, без /arch:AVX* |
avx2 |
/O2 /arch:AVX2, без vectorizer-disable флагов |
/d2Qvec- — внутренний, не документированный публичным контрактом MSVC ключ.
Здесь он означает проверенный на зафиксированной версии компилятора
project-novec-профиль, но не гарантирует отсутствие всех SIMD-инструкций: x64 ABI,
compiler runtime и CRT могут использовать базовый SSE2 или собственный dispatch.
Методика
benchmark.bat quick запускал профили последовательно в фиксированном порядке
scalar -> baseline -> avx2. В каждом профиле suites выполнялись как
tune -> adapt -> compare -> index:
- quick использует 5 повторов;
- tuning: размеры 10 000, 100 000 и 1 000 000;
- comparison: размеры 1 000, 10 000, 100 000 и 1 000 000;
- кандидаты одного повтора получают один и тот же заранее материализованный trace; стартовый кандидат циклически меняется между повторами;
- analyzer проверяет согласованность checksum для сопоставимых успешных строк;
- слишком дорогие комбинации
deque/listмогут получитьskipped_cost_guardи не входят в соответствующую aggregate-ячейку.
Process affinity/pinning не задавались. Порядок профилей не рандомизировался, а план питания оставался Balanced. Поэтому результаты следует читать как инженерный quick-checkpoint, подверженный drift температуры, частоты и фоновой нагрузки, а не как окончательную публикационную оценку.
std::deque и std::list здесь только внешние фиксированные baselines. Они
никогда не являются внутренними режимами AdaptiveSequence; у list измеряется
индексная семантика с линейным проходом.
Проверка адаптации
Финальный запуск валидатора прошёл:
- 282 compare-ячейки;
- 48 adapt-ячеек.
Текстовый итог повторной проверки сохранён в validation.txt.
Для всех suites дополнительно проверены результаты одинаковых трасс: расхождений
checksum внутри профиля и между scalar/baseline/avx2 нет. Полнота raw
матрицы: по 1125 строк tune, 800 adapt, 4700 compare и 270 index на профиль;
в compare по 225 строк на профиль имеют ожидаемый skipped_cost_guard, остальные
статусы — ok.
Для короткого negative control допускаются ноль переключений и ноль
leaf-rebuilds; для stationary default — не более одного. У явно двухфазной трассы
phase_10_uniform_10_local_80_read есть две разные edit-фазы. Первоначальный
лимит одной leaf-rebuild ошибочно трактовал их как одну фазу; лимит был исправлен
до двух только для этой структуры. Измерительные CSV не менялись, и после
исправления спецификации валидатор прошёл весь набор. Этот pass доказывает
ограниченность transition churn на проверяемых трассах, но сам по себе не
доказывает оптимальную производительность policy.
Проверка выполняется по медианам пяти повторов. В raw compare есть один
seed-specific случай phase_uniform_local_read, N=1M с тремя leaf-rebuilds;
медиана этой ячейки равна двум. Это не stationary churn, но показывает, что
следующий holdout должен валидировать не только медианы, а каждый повтор.
Основные результаты
В aggregate-compare.csv score каждой ячейки нормирован
по лучшему фиксированному baseline в этой ячейке; выше — лучше.
| Кандидат | Overall score | Eligible cells |
|---|---|---|
| forced tiered, leaf64 | 0.395052 | 282 |
| forced tiered, leaf128 | 0.391598 | 282 |
| adaptive deferred | 0.347315 | 282 |
| adaptive eager | 0.326839 | 282 |
| reserved vector | 0.146329 | 282 |
| external deque | 0.080267 | 267 |
| external list-index | 0.012763 | 162 |
adaptive deferred превосходит reserved vector в aggregate этой конкретной
quick-матрицы, но проигрывает лучшим forced leaf-кандидатам. Победа над лучшим
фиксированным oracle или над всеми структурами не доказана.
В терминах именно aggregate score adaptive выше reserved vector в 2.37 раза, но ниже leaf64 на 12.1% и leaf128 на 11.3%. Это отношения иерархически агрегированных нормированных score, а не прямые коэффициенты ускорения времени. Прямое парное агрегирование времени даёт ту же картину: adaptive быстрее vector примерно в 2.37 раза, но медленнее leaf64 на 13.74% и leaf128 на 12.75%; leaf256 он уже опережает на 1.07%. После hysteresis stationary-ячейки имеют не более одного storage switch и одной leaf-rebuild; два переключения/две rebuild встречаются только там, где trace явно содержит burst или две разные edit-фазы. Directory-only rebuild в выбранной policy в этом run не потребовался.
Главный оставшийся regret — не повторные переключения, а cold adaptation. Для
blob64 и non-trivial string при N=100k policy делает один переход без rebuild,
но до него успевает выполнить дорогие O(N) vector edits и затем платит за
конвертацию; против заранее подготовленного leaf64 это в среднем примерно 2.45x
и 4.03x медленнее соответственно. Есть и противоположная слепая зона:
read_99, N=1M остаётся vector из-за 5% entry threshold, хотя редкие edits уже
достаточно дороги, чтобы fixed tiered выиграл. Следующий вариант policy должен
использовать накопленный regret/стоимость edit, а не только их долю.
В aggregate-adapt.csv лучшие fixed варианты — leaf128
(0.817859) и leaf256 (0.788496); лучший адаптивный policy moderate deferred
получил 0.216999. Adapt-сценарии остаются главным источником regret.
При этом на длинной exact phase moderate примерно в 11.1 раза быстрее vector,
но всё ещё в 5.27 раза медленнее заранее tiered oracle; на коротком negative
control ложных переходов нет, а цена сбора статистики составляет около 8.5%.
Tuning выбрал leaf128 как сильнейший quick-кандидат во всех трёх профилях:
| Профиль | Лучшие параметры | Score |
|---|---|---|
| scalar | leaf128, levels4 | 0.878968 |
| baseline | leaf128, levels3 | 0.873461 |
| avx2 | leaf128, levels3 | 0.887906 |
Это выбор для измеренной матрицы, а не универсальная константа. Index-suite также показал большой выигрыш специализированного flat hash index над линейным lookup vector, но только в lookup-подзадаче и ценой дополнительной памяти; переносить этот вывод на контейнер целиком нельзя.
Лучший единый tuning-компромисс трёх профилей — leaf=128, fanout=64, levels=3; вариант с levels=4 отстал по cross-profile score всего на 0.276%,
поэтому точную глубину пока нельзя считать надёжно определённой. Leaf, напротив,
устойчивее: 14 из 15 логических tuning-ячеек выбрали одинаковый leaf во всех
профилях. Random access всегда предпочёл 1024, большинство edit/mixed-ячеек —
64, а localized edit при N=1M — 128.
AVX2 не дал общего выигрыша для контейнерного mix: для tuning-конфига 128/64/3 AVX2 и baseline различались лишь на 0.26% по cellwise geometric mean. На compare AVX2 ускорил reserved vector примерно на 8.9%, но fixed tiered — только на 2–3%, а adaptive deferred оказался на 0.9% медленнее baseline. Порядок профилей не рандомизирован, поэтому проценты следует считать инженерным сигналом, а не изолированным доказательством эффекта SIMD.
Отдельно отмечен устойчивый codegen outlier: AVX2 adaptive-deferred sequential
uint32 во всех четырёх N оказался в 1.85–1.98 раза медленнее baseline во всех
пяти повторах, при совпадающих checksums. До профилирования generated code это
нельзя объяснять как общий недостаток AVX2 или как шум одной выборки.
Hash index выиграл у linear lookup во всех 27 проверенных index-ячейках, но его геометрическое среднее потребление памяти составило 9.26x от payload vector (диапазон 7.00x–17.49x). Экстремальные коэффициенты скорости неустойчивы: при N=1M выполнялось только 50 запросов, построение индекса не входило в таймер, а свежепостроенный hash был cache-hot. Поэтому этот suite подтверждает пользу опционального индекса качественно, но не является точной оценкой production ускорения.
Артефакты и provenance
*-raw.csv— отдельные измерения;*-summary.csv— summary внутри профиля;aggregate-*.csvиcell-medians-*.csv— объединённые результаты;environment.json/environment.txt— машина, toolchain, флаги и порядок профилей;source-manifest.sha256— 23 файла workspace на момент запуска;binary-manifest.sha256— 9 измерявшихся EXE (uc_demo,uc_tests,uc_benchдля трёх профилей).
В metadata нет Git HEAD, а workspace на момент запуска имел 12 dirty entries,
поэтому manifests — основной источник provenance. Они являются неизменяемым
снимком именно момента измерения. После run были дополнены docs/findings.md и
исправлена только спецификация лимита в tools/validate_adaptation.ps1; это два
текущих расхождения с source manifest. C++-исходники и все девять Release EXE
по-прежнему совпадают со снимком. Манифесты намеренно не пересоздаются задним
числом.
Ограничения
- это quick с пятью повторами, не full и не независимый multi-seed holdout;
- нет pinning/affinity, фиксированного high-performance power plan и рандомизированного порядка профилей;
- segmented backend пока одноуровневый prototype, а не завершённая многоуровневая структура;
- policy всё ещё заметно уступает лучшему fixed leaf oracle на adapt workloads;
- память учитывается в benchmark, но для окончательного вывода нужен отдельный контролируемый memory-budget experiment;
deque/list— внешние baselines с cost-guard пропусками, а не альтернативные режимы адаптивного контейнера.