docs(diarization): спроектирован следующий CPU-эксперимент #40

Merged
ddmitry merged 2 commits from wayfinder/issue-34-cpu-experiment-matrix into master 2026-08-31 01:05:09 +03:00
@@ -0,0 +1,550 @@
# Следующий CPU-эксперимент диаризации
**Дата:** 2026-08-30
**Статус:** согласованная постановка эксперимента. Production-конфигурация не
менялась.
## Проблема
Текущий профиль Pyannote FP32 + WeSpeaker тратит большую часть времени
диаризации на эмбеддинги для сильно перекрывающихся окон. Отдельные исследования
нашли три независимых рычага: увеличить шаг окна, удешевить эмбеддинги и изменить
их исполнение. Полный перебор всех сочетаний слишком дорог и смешивает проверку
механизма с выбором итогового профиля.
Нужен один эксперимент с последовательным отсевом. Он должен показать, есть ли
комбинация, которая сокращает время диаризации на Ryzen минимум на 30%, сохраняет
принятую проверку качества и остается работоспособной на старом Intel. Ориентир
для сильного результата - ускорение примерно в два раза.
## Сначала прочитать
- [ADR-007](../adr/007-word-level-speaker-diarization.md) - когда работа касается
действующего профиля и проверки качества.
- [Предыдущий CPU-тест](../benchmarks/2026-08-30-lightweight-diarization-cpu.md) -
при работе с корпусом, метриками и приватными результатами.
- [Шаг окна](../research/2026-08-30-sherpa-window-shift.md) - при добавлении
`window_shift_ratio` и счетчиков работы.
- [Оценка числа говорящих](../research/2026-08-30-diarization-speaker-count.md) -
при реализации `Prepare/Finalize`, eigengap и NME.
- [Embedding-инференс](../research/2026-08-30-diarization-embedding-inference.md) -
при квантизации, конкурентных stream и batch TitaNet.
- [Решение об аппаратной матрице](https://git.dementev.space/ddmitry/local-transcriber/issues/35#issuecomment-2243)
- при подготовке машин и разборе памяти.
## Цель и границы
Эксперимент должен:
- расширить исследовательский стенд для новых ручек, повторов и счетчиков;
- независимо проверить сокращение работы, модель с надежной оценкой числа
говорящих и способы ускорить embedding-инференс;
- собрать не больше трех комбинаций на Intel, передать их в Ryzen-этап и
оставить там не больше двух финалистов после первичного A/B;
- выбрать один итоговый вариант либо зафиксировать, почему ни один вариант не
прошел;
- выпустить публичный обезличенный отчет и сохранить полную переносимую капсулу
в закрытой папке Яндекс Диска.
В эксперимент не входят:
- изменение CLI, `.transcriber.toml` или модели по умолчанию;
- автоматический профиль по ISA или отдельные пользовательские профили;
- параллельный запуск ASR и диаризации, GPU, NPU и Mac;
- новая полная спектральная кластеризация, NeMo runtime и k-means;
- CAMPPlus batch без маски и несколько сессий как самостоятельное направление;
- production-переход на победителя по результату одного эксперимента.
Если каскад не достигает нужной скорости из-за качества, следующий шаг -
отдельная карта по end-to-end диаризации. Этот эксперимент ее не начинает.
## Совпадающий baseline
Каждое сравнение использует baseline, запущенный на той же машине, в том же
режиме питания и на том же стенде:
- `sherpa-onnx` 1.13.6;
- Pyannote Segmentation 3.0 FP32;
- WeSpeaker ResNet34 LM FP32;
- `window_shift_ratio=0.1`;
- `FastClustering` с порогом `0.89` и автоматическим числом кластеров;
- последовательный embedding-инференс;
- не больше `P` потоков, где `P` - число физических ядер машины.
Версию `sherpa-onnx`, uv override и `sherpa-onnx-core` обновляют синхронно.
Значение `0.1` сохраняет действующее поведение. Изменение зависимости само по
себе не считается ускорением. На тех же трех фрагментах W0 сравнивается со
свежим запуском 1.13.5 на той же машине. После канонической нумерации кластеров
сверяются интервалы, назначение говорящих словам и прежние диагностические
метрики. Различия направляются в слуховую проверку отмеченных мест. Baseline
1.13.6 принимается только при `3 / 2 / 2` и отсутствии подтвержденной регрессии.
Время диаризации во всей спецификации означает wall time одного вызова
`diarizer.process(samples)`. ASR, загрузка моделей, декодирование и подготовка
данных в него не входят.
Три порога используются во всех этапах:
- разница до 5% считается равенством по скорости;
- 10% - порог продолжения после дешевого отсева;
- 30% - итоговый порог `go` на Ryzen.
## Стенд и данные
Существующий `scripts/benchmarks/diarization_calibration.py` остается точкой
входа, но получает новую версию manifest. Прежний формат жестко закрепляет
матрицу FP32/INT8 x WeSpeaker/TitaNet, обязательный свип порогов и
`sherpa-onnx` 1.13.5, поэтому старые результаты с новым экспериментом не
продолжаются.
Новая версия manifest и идентификатор ячейки должны учитывать:
- вариант шага окна, модель, счетчик `N` и способ embedding-инференса;
- число outer workers, вид session и бюджеты intra-op/inter-op;
- размер batch, где он применим;
- машину, число физических ядер, CPUID/ISA и версии библиотек;
- номер повтора и позицию в расписании;
- этап `intel` или `ryzen`; для Ryzen - исходный commit handoff;
- хеш экспериментальной сборки или патча sherpa-onnx;
- хеши моделей и приватных входов по прежнему контракту.
Для QDQ идентичность также включает хеши калибровочных источников,
препроцессинг, reader, параметры `quant_pre_process` и `quantize_static`, список
исключенных узлов, версии инструментов и хеш готового графа. Для каждого
калибровочного элемента manifest хранит идентификатор и SHA-256 исходной записи,
а также использованный временной интервал. Вырезанный или перекодированный файл
наследует эти данные от исходника. Ни один калибровочный элемент не может
происходить из трех контрольных записей, даже из другого интервала.
`--validate-only` отклоняет общий исходник; если происхождение нельзя подтвердить
или отдельного материала нет, ветвь QDQ пропускается и не блокирует остальные
направления. Проверка показывает только хеши и рецепт без приватных путей.
Каждая измеряемая ячейка выполняется в новом дочернем процессе. Стенд сохраняет
сырые результаты каждого повтора и не заменяет их одной агрегированной строкой.
При продолжении запуска успешные ячейки пропускаются по стабильному
идентификатору, а расписание оставшихся сохраняет заданный A/B-порядок. Режим
`--validate-only` показывает полный план без приватных путей и текста.
### Приватная капсула
Капсула передается одним ZIP-архивом; `capsule_sha256` означает SHA-256 этого
файла. В корне лежит `capsule.json` с идентификатором капсулы, исходным commit,
хешем публичного Intel-отчета и SHA-256 каждого файла внутри. Все пути в manifest
относительны корня распакованной капсулы.
Архив содержит:
- manifest и полный набор входов для трех пятиминутных и трех полных прогонов;
- ASR-sidecar, опорные транскрипты и разметку, нужную для метрик и прослушивания;
- калибровочный материал и готовый QDQ-граф, если QDQ передается в Ryzen;
- сырые Intel-результаты, интервалы, worker requests и журналы, нужные для
продолжения и разбора;
- локально созданные или измененные модельные артефакты.
Публично загружаемые базовые модели можно не дублировать в архиве, если manifest
фиксирует репозиторий, revision и SHA-256. После распаковки `--validate-only`
сверяет внешний хеш архива, `capsule.json`, относительные пути и все внутренние
хеши до запуска моделей.
### Приватность
За пределами Git остаются manifest, записи, WAV-кеш, ASR-sidecar, опорные
транскрипты, сырые интервалы, результаты, worker requests, журналы и материалы
слуховой проверки. Рабочий каталог стенда нельзя размещать в обычной
`.scratch`: репозиторий игнорирует там только служебный каталог
`.scratch/handoffs/`, не связанный с переносом эксперимента между ноутбуками.
Публичный отчет содержит хеши, агрегаты, конфигурацию и выводы. В него не входят
локальные пути, распознанный текст, временные интервалы, имена участников и
фрагменты приватных записей. Перед внешним ревью используется только публичный
отчет и код стенда.
## Разделение работы между ноутбуками
Эксперимент выполняют две зависимые задачи. Один агент не меняет машину в ходе
работы:
| Задача | Машина | Этапы | Результат |
|---|---|---|---|
| [Intel #41](https://git.dementev.space/ddmitry/local-transcriber/issues/41) | Core i7-6820HQ | 0-4 | стенд, Intel-отчет и handoff |
| [Ryzen #42](https://git.dementev.space/ddmitry/local-transcriber/issues/42) | Ryzen 7 8845H | 5-6 | финальный отчет и решение |
Intel-агент коммитит в обычную ветку своей задачи код стенда и обезличенный
отчет. Специальная общая ветка для handoff не создается. Manifest, сырые
результаты и другие приватные материалы в Git не попадают. В финальном
комментарии к Intel-задаче агент оставляет только индекс:
- `intel_outcome`: `handoff` или `stop-before-ryzen`;
- ветку, точный commit и PR;
- путь и SHA-256 публичного Intel-отчета;
- число кандидатов и ссылку на их рецепты в отчете;
- непрозрачный идентификатор приватной капсулы и ее SHA-256;
- ссылку на ограничения в отчете.
При расхождении источником истины служит файл отчета в указанном commit,
комментарий остается указателем. Он не содержит приватных путей, текста или
интервалов. Пользователь передает капсулу на второй ноутбук отдельно и сливает
Intel-PR обычным merge, без squash или rebase. После этого #41 закрывается и
средствами Gitea разблокирует #42. Ryzen-агент сначала читает обе задачи и все
комментарии к первой, обновляет `master` и проверяет присутствие handoff-commit
в его истории. Затем он сверяет SHA-256 капсулы и запускает `--validate-only`.
При наличии кандидатов агент создает обычную ветку #42 от `master`.
Несовпадение commit, капсулы или плана переводит задачу в `needs-info`;
недостающие данные агент не восстанавливает догадками.
Штатный путь идет только с Intel на Ryzen. Если на Ryzen обнаружена ошибка
стенда, которая делает Intel-результаты недействительными, эксперимент
останавливается и требует отдельного исправления. Обычный отсев или проигрыш
кандидата не возвращает работу на Intel.
## Воронка вариантов
### Этап 0. Проверить механизмы без дорогих замеров
До CPU-матрицы стенд должен пройти дешевые проверки:
1. `sherpa-onnx` 1.13.6 импортируется, принимает пять значений шага и сохраняет
прежнее поведение при `0.1`.
2. Счетчик eigengap и NME проходят синтетические фикстуры из исследования.
Затем оба считаются на независимых копиях трех матриц CAMPPlus и трех матриц
TitaNet, подготовленных без повторного инференса. Для каждой модели отдельно
остается счетчик, который дает точные `3 / 2 / 2`. Если проходят оба, при
разнице времени до 10% выбирается более простой eigengap, иначе более
быстрый. Модель, для которой не проходит ни один счетчик, выбывает из
автоматического режима.
3. `Prepare/Finalize` сохраняет пары строк и штатную финализацию. Для известного
`N` метки строк и итоговая разметка совпадают с отдельным штатным вызовом на
той же матрице.
4. WeSpeaker S8S8 QDQ сохраняет входы, выходы, opset, пользовательские метаданные
и загружается через `SpeakerEmbeddingExtractor`.
5. Shared-session возвращает по одному результату на каждый вход без гонок,
пропусков и нечисловых значений. Сравнение ведется с последовательными
вызовами на том же наборе входов.
Числовой дрейф эмбеддингов записывается, но отдельный произвольный порог для него
не вводится. Решение принимается по одинаковому числу кластеров, меткам строк и
итоговой разметке.
### Этап 1. Найти безопасный шаг окна на Intel
На Core i7-6820HQ проверяется текущая связка WeSpeaker с сеткой:
| Ячейка | `window_shift_ratio` | Роль |
|---|---:|---|
| W0 | `0.10` | совпадающий baseline |
| W1 | `0.15` | осторожное сокращение окон |
| W2 | `0.20` | примерно половина исходных окон |
| W3 | `0.25` | агрессивный кандидат |
| W4 | `0.50` | граничная проверка |
Для каждой ячейки выполняется один прогон трех пятиминутных фрагментов. Стенд
записывает фактический шаг в сэмплах, расчетное число segmentation-окон и число
embedding-заданий. Ускорение без соответствующего изменения счетчиков считается
необъясненным и не проходит дальше до разбора.
Все значения, прошедшие обязательные условия, ранжируются по времени
диаризации. При равенстве по скорости выбирается меньший шаг: он меньше отходит
от принятой разметки. Если лучший по времени шаг ухудшает purity или число слов
без говорящего, до сборки комбинаций прослушиваются отмеченные пятиминутные
случаи. Рабочим становится лучший шаг, достигший порога продолжения. Если такого
нет, рабочим остается `0.1`, а ветвь шага считается проигравшей.
### Этап 2. Выбрать модель и счетчик говорящих на Intel
При `window_shift_ratio=0.1` сравниваются:
| Ячейка | Эмбеддинги | Оценка числа говорящих |
|---|---|---|
| M0 | WeSpeaker FP32 | текущий `FastClustering`, порог `0.89` |
| M2 | CAMPPlus | счетчик, выбранный для CAMPPlus, + `FastClustering(N)` |
| M3 | TitaNet small | счетчик, выбранный для TitaNet, + `FastClustering(N)` |
M1 намеренно отсутствует: отдельный контроль `Prepare/Finalize` перенесен в
дешевую проверку этапа 0 и не получает CPU-ячейку.
M0, M2 и M3 получают по одному прогону трех фрагментов. Быстрая модель проходит
дальше, если выдерживает обязательные условия и достигает порога продолжения
относительно M0. Малая разница не уточняется дополнительными повторами.
Регрессия диагностических метрик требует прослушать отмеченные пятиминутные
случаи до сборки комбинации.
### Этап 3. Проверить исполнение эмбеддингов на Intel
#### QDQ WeSpeaker
S8S8 QDQ сравнивается с FP32 на Intel при шаге `0.1`; достаточно одного A/B по
трем фрагментам. Если QDQ проходит обязательные условия и проверку памяти, он
остается допустимым для C2 даже без порога продолжения на Intel: возможный
выигрыш на Ryzen зависит от VNNI/AVX-512. Изолированный Ryzen A/B выполняется
уже после handoff, в этапе 5.
U8U8 допускается как единственный запасной вариант, если S8S8 достигает порога
продолжения по времени диаризации, но не проходит качество. Другие форматы
квантизации в матрицу не добавляются.
#### Shared-session streams
На Intel сравниваются четыре конфигурации при общем бюджете `P`; достаточно
одного A/B по трем фрагментам. Лучшая shared-session конфигурация, прошедшая
обязательные условия и проверку памяти, остается допустимой для C2 даже без
порога продолжения. Изолированный Ryzen A/B выполняется в этапе 5.
| Ячейка | Outer workers | Intra-op | Session |
|---|---:|---:|---|
| E0 | `1` | `P` | одна, текущий внутренний параллелизм |
| E1 | `2` | общий pool `P` | одна |
| E2 | `P` | `1` | одна |
| E3 | `P` | `1` на session | отдельные session, отрицательный контроль |
`inter_op_threads=1` во всех ячейках. Измеряется время полного набора
embedding-вызовов. Latency одного удачного stream для решения не используется.
E3 не становится кандидатом, если только не опровергает ожидаемый рост RSS и не
выигрывает у shared-session по времени диаризации.
#### Batch TitaNet
Batch TitaNet открывается только после прохождения M3. Сначала сравниваются batch
`1` и `4` на одинаковом наборе переменных длин. Batch проходит к CPU-замеру,
если сохраняет число кластеров, метки строк и итоговую разметку на всех трех
контрольных матрицах. CAMPPlus batch и подбор размера больше `4` отложены.
### Этап 4. Собрать комбинации на Intel
Полного декартова перебора нет. На Intel собираются максимум три явно заданные
комбинации:
| Ячейка | Состав |
|---|---|
| C0 | рабочий шаг + WeSpeaker FP32 + последовательный инференс |
| C1 | рабочий шаг + сильнейшая ветвь по Intel |
| C2 | рабочий шаг + заранее определенный ISA-стек |
Для C1 рассматриваются ветви, достигшие порога продолжения на Intel; сильнейшая
определяется по сокращению времени диаризации. C2 начинается с рабочего шага и
WeSpeaker FP32. Если QDQ выдержал обязательные условия и память, он заменяет
FP32 независимо от скорости на Intel. Затем добавляется лучшая допустимая
shared-session конфигурация для WeSpeaker. Если ни QDQ, ни shared-session не
допустимы, C2 собирается как совместимый стек прошедших Intel-ветвей. Так
ISA-кандидаты доходят до Ryzen без предварительного Ryzen-замера. Batch совместим
только с TitaNet. Если C1 и C2 совпадают, дубликат не запускается. C0 совпадает
с baseline и тоже не запускается, если рабочим остался шаг `0.1`.
Каждая существующая комбинация получает один прогон трех фрагментов на Intel.
Все комбинации, выдержавшие обязательные условия и память, передаются в handoff;
их остается не больше трех. Если прошел только новый шаг окна, передается один
C0. Если новых комбинаций нет, Intel-агент фиксирует `stop-before-ryzen`:
Ryzen-задача подтверждает итог по handoff и не запускает модели.
### Этап 5. Принять финалиста на Ryzen
Ryzen-агент начинает с восстановления handoff. Если передано ноль кандидатов и
Intel-итог равен `stop-before-ryzen`, после проверки commit, SHA-256 капсулы и
`--validate-only` он фиксирует итог `stop` и закрывает задачу без запуска
моделей. При наличии кандидатов агент воспроизводит совпадающий baseline и
выполняет изолированные QDQ и shared-session A/B для используемых механизмов.
Каждый такой A/B состоит из одного прогона baseline и одного прогона кандидата
на трех фрагментах. Порядок baseline и кандидата меняется между парами. Эти A/B
проверяют обязательные условия и объясняют вклад механизма. Проигрыш по скорости
сам по себе не снимает переданную комбинацию; до первичного A/B она выбывает
только при нарушении обязательных условий или правила памяти.
Затем первичный A/B получают все переданные комбинации, но не больше трех.
Каждый A/B состоит из одного прогона baseline и одного прогона кандидата на трех
фрагментах. Порядок меняется для следующей пары. Вариант, который нарушает
обязательные условия или не достигает порога продолжения, выбывает без
дополнительных прогонов.
Не больше двух лучших вариантов, прошедших первичный A/B, получают по пять
чередующихся A/B-циклов. Они выбираются по времени диаризации, при равенстве по
скорости - по меньшему RSS, затем по меньшему числу отличий от baseline. При
полном равенстве порядок C0, C1, C2 служит последним тай-брейком. Один цикл
содержит все три фрагмента; результат цикла - сумма времени диаризации. Итогом
служат медиана пяти парных ускорений и MAD. Доверительные интервалы и новые
повторы после пяти циклов не требуются.
Итоговая комбинация проходит условие скорости, если достигает итогового порога
на Ryzen. Примерно двукратное ускорение остается ориентиром. На Intel процент
допустимого замедления заранее не задается. Если финалист медленнее Intel
baseline минимум на порог продолжения, эксперимент возвращает `needs-decision`
вместо автоматического `go`: приемлемость фактической регрессии решает владелец
после замеров.
### Этап 6. Проверить полные записи
Один победитель и совпадающий baseline 1.13.6 однократно обрабатывают три полные
записи на Ryzen. Эти прогоны проверяют перенос результата за границы
пятиминутных фрагментов; единственное время полного прогона не заменяет
итоговый порог скорости.
В слуховой проверке baseline и кандидат сопоставляются по пяти группам:
- короткие ответы и перебивки;
- смены говорящего;
- перекрывающуюся речь;
- устойчивость соответствия голоса кластеру;
- слова без говорящего и малые остаточные кластеры.
Вердикт и краткие причины сохраняются в приватной капсуле. Публичный отчет
содержит только итог `пройдено` или `не пройдено` по каждой категории.
## Метрики
### Работа и скорость
Для каждой ячейки сохраняются:
- wall time, user/system/total CPU, средняя загрузка ядер и RTF;
- времена segmentation, embeddings, clustering и всего вызова;
- фактический шаг, число segmentation-окон и embedding-заданий;
- число строк подготовленной матрицы, выбранные `p` и `N`, время счетчика;
- число embedding-вызовов, batch, workers, session и бюджеты потоков.
### Качество
Обязательные условия:
- точное число кластеров `3 / 2 / 2` на контрольных фрагментах;
- неизменный распознанный текст с точностью до прежней нормализации пробелов;
- повторяемые `p`, `N`, метки строк и финализация на одной матрице;
- конечные эмбеддинги и отсутствие потерь или дублей входов.
Диагностика:
- `mapped speaker purity` на двух записях с опорной разметкой;
- число слов без говорящего;
- число и длительность малых кластеров;
- доля времени речи в кластерах сверх ожидаемых;
- доля назначенных слов в тех же кластерах.
Последние два показателя получают разные имена и единицы. Дополнительными
считаются кластеры после `N` крупнейших по времени речи. Доля времени использует
общее время речи в знаменателе, доля слов - все слова, назначенные известным
кластерам.
Прокси-метрики не получают произвольных жестких порогов. Ухудшение purity или
рост слов без говорящего относительно совпадающего baseline помечает случаи для
слуховой проверки. Победитель не принимается, если слуховая проверка
подтверждает регрессию коротких реплик, смен говорящего или устойчивости
кластеров.
ASR выполняется один раз при подготовке sidecar и не входит в измеряемые ячейки.
Каждая ячейка использует тот же набор слов. Проверка неизменности текста
сравнивает сборку слов после назначения говорящих с sidecar и ловит потерю,
дубль или перестановку слов; она не запускает ASR повторно.
### Память и машина
Для каждого запуска фиксируются точный SKU, физические и логические ядра, RAM,
CPUID/ISA, ОС, версии библиотек, питание, power mode, governor, turbo, power
limits, температуры, throttling, swap и число потоков.
Обе машины работают от сети в стабильном `balanced`-режиме. A/B чередуются,
чтобы прогрев не давал преимущества одному варианту. Прогон с throttling
или сменой режима питания не учитывается и повторяется один раз после
стабилизации. Абсолютный температурный порог не задается.
Peak RSS записывается для каждой ячейки и сравнивается с baseline той же машины.
RSS выше 2 ГиБ на машине с 16 ГиБ RAM требует разбора, но сам по себе не
отбрасывает вариант. Правило памяти останавливает ячейку при OOM, активной
подкачке, росте RSS между повторами без плато или линейном росте памяти с числом
workers или session.
## Правила решения
Результаты рассматриваются в таком порядке:
1. обязательные условия качества и корректности;
2. медианное сокращение времени диаризации на Ryzen;
3. память;
4. сложность будущей реализации и сопровождения.
Практически незначимая разница не получает дополнительных прогонов. При
равенстве по скорости выбирается более простой и экономный по памяти вариант.
Ни один экспериментальный результат автоматически не меняет production-профиль.
`stop-before-ryzen` - промежуточный итог Intel-этапа; после проверки handoff он
становится итогом `stop`. `needs-info` - состояние заблокированной задачи, а не
результат эксперимента.
Возможны четыре итоговых исхода:
- `go`: один вариант достигает итогового порога на Ryzen и проходит все
проверки;
- `needs-decision`: Ryzen проходит итоговый порог, но Intel показывает
практически значимую регрессию;
- `stop`: baseline 1.13.6 не принят либо ни один вариант не проходит скорость,
качество или память; причины сохраняются по каждой ветви;
- `new map`: сокращение окон и цены эмбеддинга упирается в качество раньше
порога скорости, поэтому отдельно рассматривается end-to-end архитектура.
## Проверка реализации стенда
Минимальная автоматическая проверка:
```bash
uv run pytest -q \
tests/test_diarization_calibration.py \
tests/test_speaker_diarizer.py \
tests/test_diarization.py
```
Тесты должны покрывать новую схему manifest и идентичность ячеек, расписание и
повторы, смешанные результаты счетчиков, ветвь только с шагом окна, совместимый
стек, наследование происхождения QDQ-материала и запрет общего исходника с
контрольными записями, обязательные условия, два показателя остаточных кластеров,
контроль общего бюджета потоков, продолжение незавершенного запуска, границу
этапов Intel/Ryzen, не больше трех handoff-кандидатов и отсутствие приватного
текста в ошибках.
До моделей выполняются:
```bash
uv run python scripts/benchmarks/diarization_calibration.py --help
uv run python scripts/benchmarks/diarization_calibration.py \
--manifest /private/path/manifest.json \
--validate-only
```
Intel-отчет должен позволять восстановить отсев и точные рецепты переданных
комбинаций по агрегатам и хешам. Финальный Ryzen-отчет ссылается на него и
добавляет первичный A/B, финальные циклы и решение. Полное воспроизведение
использует приватную капсулу из закрытой папки Яндекс Диска.
## Отклоненные варианты
- Полный декартов перебор отклонен: большая часть ячеек повторяет уже
проигравшие механизмы и не влияет на выбор.
- Пять повторов каждой ячейки отклонены: дорогая точность нужна только для
финального A/B. Первичный отсев использует порог продолжения.
- Только шаг окна отклонен: он не проверяет более дешевые модели и исполнение,
которые могут складываться с сокращением работы.
- Только современный CPU отклонен: он скрывает регрессии на AVX2 и не дает
понять, зависит ли выигрыш от ISA.
- Полная матрица на Ryzen отклонена: старый Intel дешевле для первичного отсева.
Ryzen получает максимум три комбинации для одного первичного A/B и оставляет
максимум две для дорогих циклов.
- Сохранение текущего профиля остается допустимым итогом, если практического
победителя нет.
## Критерии завершения задач эксперимента
- [ ] Стенд и тесты реализуют описанную воронку и не меняют пользовательский
контракт диаризации.
- [ ] Intel-задача принимает baseline и завершает этапы 0-4 либо фиксирует
подтвержденную регрессию как `stop-before-ryzen`; в обоих случаях коммитит
обезличенный отчет и публикует полный handoff-комментарий.
- [ ] Ryzen-задача начинает с указанного commit и проверенной капсулы, затем
воспроизводит свой baseline без возврата к обычным Intel-замерам.
- [ ] Все пройденные и остановленные ветви имеют явную причину и ссылку на
ячейки результата.
- [ ] Финальный A/B завершен либо зафиксировано, почему до него не дошел ни один
вариант.
- [ ] При наличии победителя выполнена проверка полных записей и слуховая
приемка.
- [ ] Публичный отчет свободен от приватных данных; полная капсула сохранена
только в закрытой папке Яндекс Диска.
- [ ] Итог формулирует следующее решение: готовить production-изменение,
вынести измеренную регрессию на Intel на решение владельца, сохранить текущий
профиль или открыть новую карту end-to-end диаризации.