Files
local-transcriber/docs/benchmarks/2026-08-31-cpu-diarization-ryzen.md
T
Dmitry Dementiev 15f88b1de8 docs(diarization): отложено удаление архивов
- Зачем:
  - старые результаты решено сохранить до завершения и приемки задачи #46.
- Что:
  - архивы задач #41/#42 закреплены как временные и доступные только для чтения;
  - стенду #46 запрещено читать архивы или использовать их как основу A/B;
  - удаление перенесено на момент после приемки нового отчета.
- Проверка:
  - выполнены git diff --check и проверка формулировки в задаче #46.
2026-09-01 16:36:11 +03:00

29 KiB
Raw Blame History

CPU-диаризация: Ryzen-этап эксперимента

Дата: 2026-08-31 Статус: new map; C2 прошла проверки качества и памяти, но не достигла итогового порога ускорения 30%, а более быстрые ветви Intel уперлись в качество. Production-профиль не менялся.

Итог

На AMD Ryzen 7 8845H выполнен этап 5 эксперимента из спецификации по единственному кандидату C2 из Intel-этапа. Этап 6 не запускали: победителя нет. C2 сохраняет window_shift_ratio=0.1, Pyannote Segmentation 3.0 FP32, WeSpeaker ResNet34 LM FP32 и FastClustering(0.89). Embedding-вызовы выполняют два outer worker через одну общую ONNX Runtime session с общим пулом из восьми intra-op потоков; inter_op_threads=1.

Изолированный A/B механизма shared-session дал ускорение 24,05%. Первичный A/B C2 показал 22,90%, поэтому кандидат прошел порог продолжения 10%, и для него запустили пять чередующихся циклов. Они дали медиану 23,57% при MAD 0,62 процентного пункта, то есть ниже итогового порога 30%; по одному Ryzen-кандидату C2 итог был бы stop.

Вся воронка дала new map. W1-W4 потеряли обязательное число кластеров. C1 с CAMPPlus + NME ускорила Intel-прогон на 33,65%, но оставила два дополнительных слова без говорящего; требуемую слуховую проверку не проводили и автоматической оценкой не подменяли. Поэтому C1 не передавали на Ryzen и не считаем победителем. Вернуть ее в матрицу можно только после приватной слуховой проверки.

Все принятые ячейки прошли обязательное качество: 3 / 2 / 2 кластера, неизменный текст, 51 / 13 / 9 слов без говорящего, те же значения mapped speaker purity 0,76655 / 0,78742, 291 / 291 / 291 segmentation-окон и 531 / 414 / 440 embedding-заданий. Потерь или дублей prepared-строк не было. Пиковый RSS C2 оставался в диапазоне 410,3-418,1 МиБ; OOM, активности swap и линейного роста памяти не наблюдалось.

Полные записи и слуховая приемка по пяти категориям на этапе 6 не запускались.

Что именно проверяли

Отчет охватывает всю воронку: этапы 0-4 на Intel и этап 5 на Ryzen. На Intel проверили шаг окна, модели эмбеддингов, счетчик говорящих, квантизацию, способы параллельного исполнения и две комбинации. Таблицы ниже собирают этот путь в одном месте. Подробные хеши и исходные идентификаторы ячеек остаются в Intel-отчете.

Intel-ячейки W/M/E/C были разведочными одиночными прогонами трех пятиминутных фрагментов. Они показывают направление и причину отсева, но не дисперсию. Исключение - соседняя пара W0/C2 под runtime guard. На Ryzen сначала выполнялась одна диагностическая пара, затем первичная пара C2 и только после прохождения порога - пять чередующихся циклов.

Этап 0: пригодность механизмов

До дорогих CPU-ячеек проверили, что экспериментальный стенд вообще измеряет задуманные механизмы, не теряя строки и не меняя пользовательский результат.

Гипотеза Что проверяли Что получилось Решение
Prepare/Finalize Подготовка один раз, кластеризация копии матрицы и финализация Результат совпал со штатным process(); пропусков и дублей строк нет Использовать в стенде
Счетчик eigengap CAMPPlus и TitaNet на матрицах 531 / 414 / 440 строк CAMPPlus дал 6 / 1 / 7, TitaNet - 7 / 2 / 4 вместо 3 / 2 / 2 Исключить
Счетчик NME Те же шесть матриц без повторного embedding-инференса Обе модели дали 3 / 2 / 2; метки повторяемы, потерянных строк нет Использовать для M2 и M3
WeSpeaker S8S8 QDQ Схема, opset, метаданные, динамические длины и загрузка через sherpa-onnx Граф технически корректен; качество и скорость проверены отдельно в Q1 Допустить к CPU-ячейке
Shared-session worker Один и два worker на одном наборе prepared-входов Строки, метки и интервалы совпали; гонок и нечисловых значений нет Допустить E1/E2

Задача этапа 0 - подтвердить, что последующие CPU-ячейки сравнивают одинаковые вычисления и используют исправный экспериментальный API. Скорость измеряли на следующих этапах.

Этап 1: можно ли считать меньше окон

Гипотеза: увеличить window_shift_ratio, сократить число segmentation-окон и вместе с ними число embedding-заданий. Все варианты использовали Pyannote Segmentation 3.0 FP32, WeSpeaker FP32 и текущий FastClustering(0.89).

Ячейка Шаг Окна по трем фрагментам Embedding-задания Время, с Кластеры Решение
W0 0,10 291 / 291 / 291 531 / 414 / 440 148,220 3 / 2 / 2 baseline
W1 0,15 195 / 195 / 195 357 / 274 / 302 100,799 2 / 2 / 2 исключена: потерян кластер
W2 0,20 146 / 146 / 146 264 / 206 / 222 76,127 2 / 2 / 2 исключена: потерян кластер
W3 0,25 117 / 117 / 117 211 / 165 / 175 61,369 2 / 2 / 2 исключена: потерян кластер
W4 0,50 59 / 59 / 59 105 / 82 / 89 31,291 1 / 2 / 2 исключена: потеряны два кластера

Скорость росла именно вместе с сокращением работы, но даже осторожный W1 нарушил обязательное качество на первой записи. Рабочим остался шаг 0.1. Комбинация C0 совпала бы с baseline, поэтому ее не запускали.

Этап 2: можно ли удешевить модель эмбеддингов

Гипотеза: сохранить полный набор окон, но заменить WeSpeaker и при необходимости счетчик говорящих. В M2 и M3 сохраняли window_shift_ratio=0.1, Pyannote Segmentation 3.0 FP32 и последовательный инференс. Менялись модель эмбеддингов и счетчик говорящих. Проценты ниже посчитаны относительно W0 148,220 с.

Ячейка Рецепт Время, с Изменение RSS, МиБ Кластеры Решение
W0 (рецепт M0) WeSpeaker FP32 + FastClustering(0.89) 148,220 baseline 362,6 3 / 2 / 2 точка сравнения
M2 CAMPPlus + NME + FastClustering(N) 94,550 -36,21% 358,6 3 / 2 / 2 самый быстрый, собрать C1
M3 TitaNet small + NME + FastClustering(N) 107,205 -27,67% 511,9 3 / 2 / 2 прошла, но медленнее и тяжелее M2

M2 и M3 прошли обязательное число кластеров. Окончательная диагностика M2 появилась в C1: purity выросла, но число слов без говорящего на первой записи изменилось с 51 до 53. По спецификации это требует прослушивания отмеченных мест.

Этап 3: квантизация и исполнение эмбеддингов

Для QDQ проверяли, даст ли INT8 выигрыш старому Intel без VNNI. Q1 и E1-E3 сохраняли window_shift_ratio=0.1, Pyannote Segmentation 3.0 FP32 и текущий FastClustering(0.89). Q1 меняла модель WeSpeaker, E1-E3 - только топологию исполнения FP32-модели. Во всех вариантах inter_op_threads=1; E1-E3 использовали общий бюджет четырех физических ядер.

Ячейка Рецепт Время, с Изменение к W0 RSS, МиБ Кластеры Решение
Q1 WeSpeaker S8S8 QDQ, sequential 177,537 +19,78% 367,7 2 / 2 / 2 исключена: медленнее и потерян кластер
E1 2 outer worker, одна session, общий pool 4 139,219 -6,07% 398,6 3 / 2 / 2 передать как аппаратный кандидат
E2 4 outer worker, одна session, intra_op=1 152,167 +2,66% 551,2 3 / 2 / 2 исключена по времени и памяти
E3 4 отдельные session, intra_op=1 159,038 +7,30% 732,3 3 / 2 / 2 отрицательный контроль подтвердился

E3 измерялась составным интервалом на заранее подготовленных заданиях, поэтому ее wall time нельзя сравнивать с process() так же строго, как E1/E2. Для решения хватило отрицательного результата одновременно по времени и памяти.

TitaNet batch проверили отдельно на тех же трех матрицах:

Контроль Строк Batch 1, с Batch 4, с Число кластеров Метки строк совпали
1 531 27,175 37,588 3 нет
2 414 25,871 32,443 2 нет
3 440 22,877 28,565 2 нет

Batch 4 сохранил число кластеров, но оказался медленнее batch 1, поменял метки строк на всех трех матрицах и дал максимальный дрейф эмбеддингов 0,277-0,317. Ветвь остановили до отдельной CPU-ячейки. SHA-256 закрытого batch-result: f12bfdb5cadf80c62871c0725eac744a99825cb5d06b70fcf11e4e538ff6df72.

Этап 4: комбинации перед Ryzen

Полного декартова перебора не было. C0 совпала с baseline; из оставшихся собрали самый быстрый модельный вариант C1 и переносимый аппаратный вариант C2.

Комбинация Точный рецепт Время, с Ускорение RSS, МиБ Диагностика Решение
C1 0.1 + Pyannote Segmentation 3.0 FP32 + CAMPPlus + NME + FastClustering(N) + sequential 98,347 33,65% 399,5 3 / 2 / 2; purity выше; +2 слова без говорящего не передавать без слуховой проверки
C2 0.1 + Pyannote Segmentation 3.0 FP32 + WeSpeaker ResNet34 LM FP32 + FastClustering(0.89) + 2 outer worker + 1 shared session + pool 4 140,632 4,32% 406,0 точное совпадение с соседним W0 передать на Ryzen

У C1 процент считается от разведочного W0 148,220 с. У C2 был соседний W0 146,978 с под тем же runtime guard. C2 не достигла Intel-порога продолжения, но shared-session заранее считалась аппаратной гипотезой: Ryzen имеет восемь физических ядер и другой ISA, поэтому спецификация требовала отдельный A/B.

Handoff и воспроизводимость

Объект Значение
Merge commit в начале Ryzen-этапа 68fda7ee893265acfa00ac78b8d4c9697f9916d9
Handoff commit 42f094ccb9464742683eec9d695e5aa9a69dd4ec
Source commit эксперимента 6345cb6449d906767b6ed33e695f2ca4eeaf3008
SHA-256 Intel-отчета 8d4183be4c3ab35f2ea043f8d87ca20e16f523cb60f9c5414d974c1bc4d6b7de
SHA-256 входной Intel-капсулы a3a50d34783d24fe6cd13b154ca9813f9842fdcb08d99eab33a6a54efea1e3ae
ID закрытого Ryzen-хранилища ryzen-42-20260831
Upstream commit sherpa-onnx 1.13.6 1cb484af9b1ab05cc36f89e03dd7d76322f2e911
SHA-256 патча sherpa-onnx 1.13.6 5f12df858cd525c8a0cfe8ff95b647c3b0e915a13d45621acc4ac42660014c49
SHA-256 Intel-wheel 306499d8668ee138357f1de43a5c1c88f4dc46c845e8a92e6ad945327bcaa050
SHA-256 Ryzen-wheel baaaa2532606f1e1411e5c84655afa70107ad4b1676420953f5e67233c46a484

Входная Intel-капсула прошла полную проверку: schema v3, 199 файлов, QDQ-артефакт доступен, предупреждений нет. Ryzen-manifest без изменений зафиксировал данные Intel-капсулы: handoff, артефакты, контрольные записи, calibration, QDQ, патч, версии runtime и интервал RSS. Рецепты baseline и кандидата во всех запущенных парах совпали с переносимыми Intel-рецептами.

Ryzen-manifest, сырые результаты, рабочие артефакты стенда, guard-свидетельства и журналы сохранены в закрытой папке Яндекс Диска. Чтобы восстановить Ryzen-этап, нужны это хранилище и входная Intel-капсула; публичный отчет связывает приведенные ниже восемь результатов с закрытым хранилищем только по SHA-256.

Переданный wheel не загрузился в Ryzen WSL: он требовал GLIBC 2.43, а стенд работает с GLIBC 2.39. Для измерений wheel пересобран из того же upstream commit и патча под текущую систему; его отдельный SHA-256 приведен в таблице. Production-зависимости и lock-файл не менялись.

Стенд потребовал два исправления, не меняющих пользовательский контракт. Ранее Ryzen-запуск сравнивал свой manifest побайтово с Intel manifest.json внутри капсулы, хотя обязательные stage=ryzen, Ryzen-машина и расписание такое совпадение исключают. Теперь Intel-manifest проверяется по индексу капсулы, а Ryzen-manifest отдельно связывается с handoff и входами капсулы. Второе: temperature_celsius допускает null, когда WSL и Windows не показывают датчик температуры; отсутствие измерения больше не подменяется числом.

Стенд Ryzen

  • AMD Ryzen 7 8845H with Radeon 780M, 8 физических ядер / 16 логических процессоров;
  • 15 584 276 480 байт RAM доступны WSL, 31 944 794 112 байт установлены в Windows; swap WSL - 4 294 967 296 байт, активность во время принятых ячеек - нулевая;
  • x86_64; AVX, AVX2, AVX-512, AVX512 VNNI, FMA, SSE4.1, SSE4.2 и AES;
  • WSL2 Linux 5.15.167.4 с GLIBC 2.39, Windows 11 Enterprise 10.0.26200, Python 3.13.13;
  • sherpa-onnx / sherpa-onnx-core 1.13.6, ONNX Runtime 1.28.0, NumPy 2.4.3, ONNX 1.22.0, kaldi-native-fbank 1.22.3, psutil 7.2.2;
  • питание от сети, стабильный Windows-профиль balanced, EPP AC 25%, максимальное состояние процессора 100%, boost mode aggressive;
  • WSL не предоставляет governor; доступные интерфейсы также не показывают температурный датчик или аппаратные лимиты мощности. Температура записана как недоступная; это ограничивает тепловой guard;
  • нативные WSL-интерфейсы не отдали supply, platform profile и throttle count, поэтому для принятых ячеек использовали override LOCAL_TRANSCRIBER_RUNTIME_GUARD_JSON, заполненный проверенными Windows-фактами: AC, balanced, turbo, power policy, нулевая подкачка, отсутствие throttling. До и после принятых пар Windows-проверка подтверждала AC, неизменный balanced и отсутствие событий throttling от Microsoft-Windows-Kernel-Processor-Power. Статический override сам по себе не обнаруживает изменение внутри ячейки, поэтому внешняя проверка остается ограничением воспроизводимости;
  • baseline: одна session, один outer worker, intra_op_threads=8, inter_op_threads=1, batch 1; C2: одна shared session, два outer worker, общий intra_op_threads=8, inter_op_threads=1, batch 1.

Ячейки этапа 5

Время ячейки - сумма трех вызовов diarizer.process(), по одному на каждый пятиминутный фрагмент. ASR и загрузка моделей в измерение не входят.

Проверка Порядок Baseline, с Кандидат, с Ускорение RSS baseline / кандидат, МиБ Качество
Совпадающий baseline W0 110,998 - - 370,4 / - да
Изолированный E1 W0 -> E1 111,557 84,723 24,05% 367,3 / 412,2 да
Первичный C2 C2 -> W0 111,423 85,909 22,90% 369,6 / 410,3 да

E1 и C2 сохранили все обязательные и диагностические показатели baseline. Первичный C2 прошел порог продолжения и уложился в правило памяти.

Пять финальных A/B-циклов

Цикл Порядок Baseline, с C2, с Парное ускорение RSS baseline / C2, МиБ Качество
1 W0 -> C2 111,354 83,529 24,99% 369,2 / 413,4 да
2 C2 -> W0 108,899 83,907 22,95% 373,0 / 418,1 да
3 W0 -> C2 110,928 84,968 23,40% 373,8 / 410,9 да
4 C2 -> W0 114,985 83,342 27,52% 376,6 / 411,1 да
5 W0 -> C2 113,233 86,548 23,57% 358,6 / 413,4 да
Итог - - - медиана 23,57%; MAD 0,62 п. п. - да

Чередование не изменило вывод: даже лучший отдельный цикл остался ниже 30%, а медиана недобрала 6,43 процентного пункта. На Intel тот же механизм давал только 4,32%; Ryzen ускоряет shared-session заметнее, но недостаточно для go.

Что уже не нужно прогонять заново

Полный replay этапов 0-5 для следующей гипотезы не нужен. Долговечное доказательство составляют публичные агрегаты, хеши и карта архитектур. Закрытые хранилища задач #41/#42 и старый материал для прослушивания C1 временно сохраняются до завершения и приемки задачи #46 как архив только для чтения. Новый эксперимент их не читает. После приемки его отчета архив можно удалить: будущей зависимостью он не является.

Результат Что можно переиспользовать Когда повтор действительно нужен
W1-W4 На текущей Pyannote-модели увеличение шага теряет кластер раньше порога скорости Изменилась segmentation-модель, логика сборки окон или обязательный quality gate
M2/M3 CAMPPlus и TitaNet с NME сохраняют 3 / 2 / 2; M2 быстрее и экономнее Изменилась модель, счетчик, clustering или контрольные входы
Q1 Этот S8S8 QDQ-граф на Intel медленнее FP32 и дает 2 / 2 / 2 Новая калибровка, формат квантизации, модель или runtime
E2/E3 Больше worker и отдельные session не окупились при прежнем бюджете потоков Изменилась топология session, runtime или бюджет ядер
E1/C2 на Ryzen Для точного рецепта shared-session медиана равна 23,57%, качество и память проходят Изменилась реализация механизма, runtime, машина или режим питания
C1 Исторический замер скорости остается доказательством незакрытого gate; старый материал временно хранится только в архиве и не входит в задачу #46 Для нового измерения C1 либо повторного открытия после удаления архива заново выполнить минимальную пару W0/C1 и подготовить новый материал для прослушивания

Есть три разных вида проверки, и они не заменяют друг друга:

  1. --validate-only сверяет commit, схему, хеши, handoff и план. Модели он не запускает.
  2. uv run pytest проверяет код стенда на mock-данных. Полный Python-suite перед PR нужен, но он не повторяет аудиозамеры и не скачивает модели.
  3. Измерительная ячейка запускает реальные модели. Для новой гипотезы нужен только затронутый минимальный срез, один совпадающий baseline и первичный A/B. Пять циклов и полные записи нужны лишь кандидату, прошедшему записанные пороги.

Что осталось неизвестным

  • C1 не получила слухового решения. Из автоматических метрик нельзя вывести, допустимы ли два дополнительных слова без говорящего.
  • Для восьми принятых последовательных W0 опубликовано суммарное разложение на segmentation, embeddings, clustering и orchestration residual. Для shared-session C2 schema-compatible native total и orchestration residual остаются недоступны.
  • Полные записи не запускались: C2 не достигла порога; C1 не дошла до Ryzen.
  • Температурный датчик в WSL и Windows недоступен. Внешняя проверка питания и throttling снижает риск дрейфа, но не заменяет измерение температуры во время каждой ячейки.
  • Эксперимент проверял локальные замены внутри прежнего каскада. End-to-end архитектуры в эту матрицу не входили.

Индекс публичных доказательств

Хеши связывают обезличенные агрегаты с приватными JSON-результатами и не раскрывают пути, текст, интервалы и идентификаторы записей.

Результат SHA-256
Совпадающий baseline f50684784392cacc2d99c8aee51d01f0a67dfee3da6ea3ba5f3db749a3d5287a
Изолированный E1 A/B 24d5edae117e0619e432ef3f3019e8c66cf3516b87b80b64b8591390db25bf48
Первичный C2 A/B 142db6d06efc8d558b38e18f47f47e3e35e7df00751da2583339f99add8d75c4
Финальный цикл 1 4b5095c9e64ff0ef75bbc58516c662bedbbdb0bedf131f66cc1031c9a0ad8c7f
Финальный цикл 2 256a17b2d08510aad03d43f5c626c1a4269c9a25271ce492dc3305c4afca1888
Финальный цикл 3 b5d37c7a8b05b065c61bc9d5f1c0ab639a80a821730e505179388d90c4239caa
Финальный цикл 4 639cc9f13511a59c7240c6fb6ec6bf1c31ec26350330e3ca0c64fb721b768736
Финальный цикл 5 4ff2863a93fc22a037de5c292391b6b46de1b539cf397026b349ea5134cf1051

Решение

Итог - new map:

  1. C2 прошла обязательное качество и память.
  2. Медианное ускорение 23,57% не достигло порога 30%.
  3. W1-W4 потеряли кластер, а C1 преодолела 30% на Intel только с незакрытым диагностическим слуховым gate: сокращение окон и цены эмбеддинга уперлось в качество раньше итогового порога.
  4. По правилу спецификации следующий шаг - отдельная карта end-to-end диаризации: карта архитектур и контракт Streaming Sortformer. Карта подготовлена в задаче #45, а контракт исполняет задача #46.
  5. Полные записи и Ryzen-слуховая приемка не требуются без победителя.
  6. Production-профиль, CLI, конфигурационный контракт и модель по умолчанию остаются без изменений.