NextStatNextStat

Бенчмарк-снимки как продукт: CI-артефакты, манифесты и базовые линии

Как превратить разовый прогон в неизменяемый набор артефактов, который можно скачать, проверить по хэшам, перезапустить и сравнить.

Бенчмарки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 + размеры ключевых файлов)

Единственная команда для генерации полного пакета валидации:

bash
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:

bash
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 к полному набору артефактов, а не к «скриншоту таблицы».

Это не «бумажная работа». Это разница между маркетинговым утверждением и цитируемым набором данных.


Связанное чтение