Бенчмарк-снимки как продукт: CI-артефакты, манифесты и базовые линии
Как превратить разовый прогон в неизменяемый набор артефактов, который можно скачать, проверить по хэшам, перезапустить и сравнить.
2026-02-08 · 8 мин. чтения
Бенчмарки — это не только измерения. Это утверждения. Если утверждение нельзя воспроизвести, это не доказательство, а скриншот.
Этот пост — про «публикационный слой» нашей программы публичных бенчмарков: как мы превращаем «запустили один раз» в бенчмарк-снимок, который можно перезапустить, проверить и (со временем) цитировать.
Каноническая спецификация: публикация бенчмарков.
Аннотация. Мы рассматриваем бенчмарк-снимок как набор артефактов, а не «табличку в блоге»: сырые измерения по каждому тесту (распределения, а не только медианы), гейты корректности (доказательство того, что посчитано именно то, что заявлено), фиксированные окружения, манифесты и хэши (чтобы «скачал» означало «байты не изменились») и индекс для поиска/репликации.
1.Определения: что такое «снимок» (и чем он не является)
Снимок — это неизменяемый набор файлов, полученный при запуске бенчмарк-харнесса. В наших терминах:
- ›Харнесс — код, который выполняет рабочие процессы бенчмарков и записывает результаты
- ›ID снимка — непрозрачный идентификатор (например,
snapshot-2026-02-08), который однозначно указывает на набор артефактов - ›Сырые результаты — тайминги по тестам/повторам и дельты корректности
- ›Манифест — машиночитаемое «что, на чём и с какими настройками запускали»
- ›Гейт корректности — явная проверка, которая падает, если результаты не согласуются с эталоном
- ›Детерминистический режим — максимально стабильная генерация JSON/PDF для хэширования и диффов
Роли разделены намеренно: baseline_manifest — это минимальный машиночитаемый «паспорт» базовой линии (какая сборка NextStat, какое окружение, какие датасеты и какие suite-результаты), а snapshot_index — полный инвентарь артефактного набора (все файлы с байтами и SHA‑256).
В частности, baseline_manifest.json фиксирует:
- ›Харнесс.
harness.repoиharness.git_commit. - ›Измеряемую сборку NextStat.
nextstat.versionи опциональноnextstat.wheel_sha256(если в снимок добавлен wheel). - ›Окружение. Минимум
environment.pythonиenvironment.platform, плюс best-effort инвентарь GPU на раннерах (например, изnvidia-smi). - ›Датасеты. Массив
datasetsвыводится из suite/case JSON (по полюdataset) и дедуплицируется по паре (id, sha256), чтобы «один и тот же вход» был указан ровно один раз.
Чем снимок не является: одно число «лучшего запуска», график без сырых данных, или бенчмарк без доказательства, что вычислено ровно то, что заявлено.
2.Анатомия снимка: минимальный публикуемый набор артефактов
Как минимум, каждый снимок включает:
- ›Сырые результаты (по тестам и повторам)
- ›Сводки (таблицы/графики, построенные из сырых данных)
- ›Манифест базовой линии —
baseline_manifest.json(схемаnextstat.baseline_manifest.v1): commit харнесса, версия NextStat, окружение, а также списокdataset.id+dataset.sha256, извлечённый из suite результатов. - ›Индекс снимка —
snapshot_index.json(схемаnextstat.snapshot_index.v1): пути к артефактам, размеры и SHA‑256. Это «инвентарь байтов» для проверки скачивания и для дальнейших сравнений. - ›Гейты корректности (проверки паритета/«здравости» для валидации запуска)
- ›Зафиксированная сборка NextStat (рекомендуется): либо
nextstat.wheel_sha256в манифесте, либо сам wheel-файл (nextstat_wheel.whl) внутри снимка для DOI-публикации - ›Пакет валидации (опционально, но полезно для «доказательной» публикации): единый пакет для аудита и подписи.
- ›
validation_report.json(схемаvalidation_report_v1) - ›Опциональный
validation_report.pdf - ›
validation_pack_manifest.json(SHA-256 + размеры ключевых файлов)
Единственная команда для генерации полного пакета валидации:
make validation-packКоманда генерирует apex2_master_report.json + validation_report.json(и опциональный PDF) + validation_pack_manifest.json в tmp/validation_pack/.
См.: артефакты отчёта валидации.
3.Детерминизм: почему хэширование — это функциональность
При публикации бенчмарков критичны два свойства:
- ›Неизменяемость — snapshot ID однозначно указывает на фиксированный набор артефактов
- ›Проверяемость — внешние проверяющие могут подтвердить, что получили те же байты, что вы опубликовали
Поэтому мы вкладываемся в детерминированную генерацию артефактов:
- ›Убираем временные метки там, где это возможно (например, в
validation_report.jsonпри запускеnextstat validation-report --deterministicполеgenerated_atустанавливается вnull) - ›Стабильный порядок ключей JSON
- ›Стабильный порядок элементов в массивах, которые логически являются «множествами»
- ›Детерминированный рендер PDF по фиксированным фикстурам для аудит‑паков
Важно: индекс снимка snapshot_index.json содержит generated_at как timestamp, потому что это формат для обнаружения и инвентаризации артефактов. Детерминизм мы требуем прежде всего от тех артефактов, которые предназначены для подписи и повторной верификации как «пакет доказательств».
В CI мы рассматриваем детерминизм как инвариант: повторная генерация тех же входов должна давать побитово идентичные JSON/PDF и тот же validation_pack_manifest.json.
4.Почему CI — правильный «издатель» (и что CI не решает)
Локальные бенчмарки полезны, но они не являются доказательством публикационного качества, потому что:
- ›Дрейф окружения незаметен
- ›Состояние кэшей непредсказуемо
- ›Действия оператора не документируются
CI-запуски лучше, потому что оснастка автоматизирована, снимки согласованы и индексированы, а артефакты прикрепляются неизменно. Но CI не решает вопрос репрезентативности оборудования и не гарантирует, что CI-окружение соответствует продакшену. Поэтому мы также публикуем манифесты окружения и приглашаем к внешним репликациям.
5.Базовые линии: избегаем «движущихся целей»
Базовые линии нужны для двух задач: поиска регрессий (мы ухудшили производительность?) и анализа трендов (как меняется производительность со временем). Но базовая линия быстро становится бессмысленной, если она «плывёт» и меняется неявно.
Поэтому мы рассматриваем базовую линию как явную, версионированную ссылку: «сравнить со снимком X», а не «сравнить с тем, что запускалось на прошлой неделе».
В скелете репозитория публичных бенчмарков манифесты базовой линии это версионированные JSON-документы со схемой. Например (сокращённо):
{
"schema_version": "nextstat.baseline_manifest.v1",
"snapshot_id": "snapshot-2026-02-08",
"deterministic": true,
"harness": { "repo": "nextstat-public-benchmarks", "git_commit": "…" },
"nextstat": { "version": "0.9.0", "wheel_sha256": "…" },
"environment": { "python": "3.13.1", "platform": "Linux-6.8…" },
"datasets": [{ "id": "hep/simple_workspace.json", "sha256": "…" }],
"results": [{ "suite": "hep", "path": "out/hep_simple_nll.json", "sha256": "…" }]
}Важно не то, какие именно поля вы выберете, а то, что базовая линия является именованной и хэшируемой ссылкой.
6.Индексация: делаем снимки обнаруживаемыми (и сравнимыми)
Набор артефактов, который нельзя обнаружить и однозначно идентифицировать, не является «публичным». Поэтому мы используем минимальный JSON-формат «индекса снимков» (схема nextstat.snapshot_index.v1), который связывает:
- ›название suite
- ›git SHA/ref
- ›метаданные workflow
- ›пути к артефактам и SHA-256 хэши
Этот индекс также является якорем для репликации третьими сторонами: если нельзя указать, что именно опубликовано, его нельзя воспроизвести.
6.1.Публикация снимка (seed-харнесс)
В опорном репозитории публичных бенчмарков снимок собирается одной командой‑публикатором, которая запускает suites, валидирует JSON‑схемы и пишет baseline_manifest.json и snapshot_index.json:
python3 benchmarks/nextstat-public-benchmarks/scripts/publish_snapshot.py \
--snapshot-id snapshot-2026-02-08 \
--out-root manifests/snapshots \
--deterministic \
--fitЭтот шаг создаёт директорию manifests/snapshots/<snapshot_id>/ с артефактами suites, а также выполняет валидацию suite JSON по схемам из manifests/schema/.
По умолчанию seed‑публикатор держится лёгким и запускает HEP + pharma suites, если не передан ни один флаг suite. Для включения дополнительных наборов используются явные флаги (например, --bayesian или --ml).
Помимо машиночитаемых JSON, публикатор также генерирует короткие markdown‑сводки вида README_snippet*.md для человека. Эти файлы намеренно не заменяют JSON‑артефакты и не являются «источником истины» для сравнения.
Чтобы зафиксировать именно измеряемую сборку NextStat, можно приложить wheel: он копируется в снимок как nextstat_wheel.whl, а его SHA‑256 попадает вbaseline_manifest.json.
7.Как внешний участник проверяет снимок (рецепт)
Снимок должен быть проверяемым без доверия к нашему посту. Минимальная петля проверки выглядит так:
- ›1) Скачать опубликованный набор артефактов (сырые результаты + манифесты)
- ›2) Проверить хэши (из
snapshot_index.jsonилиvalidation_pack_manifest.json) - ›3) Провалидировать JSON-схемы (например,
validation_report_v1) - ›4) Перезапустить харнесс на своей машине (та же suite + те же dataset IDs)
- ›5) Сравнить хэши артефактов и/или семантические дельты с оригиналом
Программа бенчмарков устроена так, чтобы «верификация» по большей части была операциями над файлами, а не субъективной интерпретацией.
8.DOI и CITATION.cff: когда бенчмарки становятся цитируемым доказательством
Когда снимок достаточно стабилен, чтобы ссылаться на него в статье или техотчёте, мы публикуем его с DOI (например, через Zenodo), добавляем CITATION.cff и привязываем DOI к полному набору артефактов, а не к «скриншоту таблицы».
Это не «бумажная работа». Это разница между маркетинговым утверждением и цитируемым набором данных.
Связанное чтение
- ›Trust Offensive: публичные бенчмарки — зачем и модель доверия
- ›Воспроизведение третьими сторонами: подписанные отчёты — независимые перезапуски как самый сильный сигнал доверия
- ›Спецификация публикации бенчмарков — протоколы, артефакты и структура наборов
- ›Артефакты отчёта валидации — JSON и PDF, которые «гейтят» каждый опубликованный снимок
