← На главную

Хаб курсов / LLM Inference Systems / глава 16

Модуль 16: Как не обмануться числом: методология замера

TTFT/TPOT перцентилями, throughput-latency Парето, препринт-гигиена, приёмочные гейты correct/min. Бенчмарк с прогревом, N=30 и доверительными интервалами.

LLM Inference Systems. Модуль 16: Как не обмануться числом: методология замера инференса

За пятнадцать модулей мы накопили десятки чисел: GFLOP/s на matmul, 172x ускорение BLAS над python-циклом, acceptance rate 79.7% у спекулятивного декодинга, латентности disaggregation-схем. Часть этих цифр перенесётся на вашу машину, часть — нет, и самое опасное то, что снаружи они неотличимы. Один и тот же заголовок «в 4 раза быстрее» может стоять за честным замером с доверительными интервалами и за одним прогоном на разогретой машине вендора. Этот модуль — о том, как отличить первое от второго: и когда читаете чужую статью, и когда замеряете сами.

Один прогон против N прогонов с ДИ: эффект значим только вне интервала

Мы разберём, какие метрики вообще стоит называть (TTFT, TPOT, перцентили вместо средних, throughput-latency Парето-фронтир), как жить с bursty-нагрузкой, которая ломает любые средние, что такое препринт-гигиена и почему «arXiv» — не синоним «проверено». Затем напишем правильный микробенчмарк с нуля: прогрев, привязка к ядру, N=30 прогонов, p50/p95/p99, доверительный интервал — и повторно замерим бенчмарк из модуля 01, чтобы увидеть, насколько «один прогон» врёт. И в конце — чек-лист чтения статьи, который можно применить к любой публикации курса.

16.1 Все цифры курса: какие переносились, а какие нет

Пройдёмся по числам, которые мы уже использовали, и честно расставим метки доверия.

Переносятся надёжно — потому что это арифметика, а не замер: 2N FLOPs на токен форвард-пасса, размер KV-кэша на токен из конфигурации модели, потолок декодинга по пропускной способности памяти, вес модели в байтах. Эти числа вы проверяли на бумаге, и они воспроизводятся на любой машине, где та же модель и тот же dtype.

Переносятся с оговорками — эмпирические константы конкретной машины: GFLOP/s вашего BLAS, ускорение квантования на вашей памяти, acceptance rate спекулятивного декодинга на вашем датасете. Acceptance rate — особенно коварная цифра: 79.7% в модуле 10 был получен на английском тексте toy-моделью. На реальном EAGLE-3 и русском тексте наш замер дал acceptance 0.17 — семнадцать процентов, при которых спекулятивный декодинг не окупается вовсе. Модель, датасет, язык, температура — каждый фактор сдвигает число на десятки процентов, и статья, которая приводит acceptance без описания всех четырёх, не даёт вам переносимой цифры.

Не переносятся вообще — вендорские и авторские headline-числа. Классика жанра: наш замер Imprint. Заявлено снижение TTFT с 19 секунд до 0.3 секунды — 60x, цифра, которая продаёт любой продукт. Независимое воспроизведение (задокументировано в EVIDENCE.md автора курса) не подтвердило заявленную величину: на нашей конфигурации выигрыш оказался на порядок меньше и сильно зависел от длины контекста. Это не обязательно значит, что авторы соврали — возможно, у них была другая длина контекста, другой батч, другая версия ядра, другой железный профиль. Но вам от этого не легче: цифру 60x вы в свой отчёт включить не можете.

Промежуточная категория — замер автора на своей системе, который нельзя проверить. Prefill-as-a-Service (PrfaaS, arXiv 2604.15039) заявляет +54% и +32% throughput против однородных базлайнов — но на внутреннем 1T-параметровом гибридном asом модели, к которой у вас доступа нет и не будет. Формально статья аккуратна: указана архитектура, описана методология. Фактически это замер автора, а не независимый: ни один внешний наблюдатель не может подставить свой кластер и получить те же +54%. Правильное чтение такой цифры: «авторы на своём железе увидели примерно столько; направление эффекта скорее всего реальное, точная величина — неизвестна». +54% может оказаться +20% на вашей топологии сети — или +80% на более bursty-нагрузке. Используйте знак и порядок, не значащие цифры.

Разновидность той же проблемы — среднее, за которым спрятан разброс. SwaT (arXiv 2610.15095) заявляет «mean 40% higher accuracy» по 17 бенчмаркам. Честнее было бы показать распределение, потому что mean 40% может быть и «стабильные +35..45% везде», и «+99% на паре бенчмарков и −20% на остальных». Cross-Model KV Transfer (arXiv 2608.03893) — редкий пример статьи, которая разброс не прячет: линейный маппер сохраняет 73–98% точности на четырёх парах моделей, но две пары деградируют резко. Авторам пришлось честно признать: метод не универсален, и «средний retention по шести парам» был бы вводящей в заблуждение цифрой, которую они сами же опровергли нелинейным MLP-хаком. Запомните этот жест: если статья показывает только mean по разнородному множеству конфигураций — это не результат, это упаковка.

Headline-дивергенция: UBTree. Одна и та же система ускоряет «в 6.94 раза» — и «в 3.428 раза», в зависимости от того, какую страницу открыть:

ИсточникЗаявленная цифраtargetSetup (как заявлено)
Официальная страница проекта (inclusion-ai.org/ubtree)6.94× avg speedupQwen3-8Bgreedy, 7 benchmarks
Аннотация статьи (arXiv:2609.39972)5.84–6.94× avg speedupQwen3-4B / Qwen3-8B7 стандартных бенчмарков
Model card (inclusionAI/Ling-3.0-flash-UBTree)3.428× avg E2E speedupLing3-Flash 124BTP4, concurrency 1, BF16, greedy, block size 16, tree 32, 6 датасетов

Это три разных конфигурационных режима: другой target/model family, другой бюджет verification-дерева (32 узла в model card против нераскрытого в аннотации), другой набор бенчмарков (7 против 6) и другой serving-setup (SGLang TP4 на Ling3-Flash против нераскрытого железа в аннотации). Ни одна из цифр не перекрывает другую, и все они — замеры авторов, а не независимая проверка; курс такими цифрами не реплицировал. Вывод для практики: фраза «UBTree ускоряет в 6.94 раза» без контекста неполна — правильная форма «авторы сообщают 6.94× на Qwen3-8B, greedy, 7 бенчмарков».

16.2 TTFT, TPOT и перцентили: язык, на котором надо говорить

Прежде чем спорить о методологии, договоримся о словаре. Три величины описывают интерактивный инференс:

  • TTFT (Time To First Token) — от отправки запроса до первого токена ответа. Определяется длиной префикса, стоимостью prefill, длиной очереди. Пользователь воспринимает его как «система задумалась».
  • TPOT (Time Per Output Token) — среднее время на каждый следующий токен после первого. Определяется декодингом, батчем, памятью. Пользователь воспринимает его как «скорость печатания».
  • E2E latency — полное время ответа, примерно TTFT + TPOT × n_tokens.

Первая ошибка в отчётах — среднее вместо перцентилей. Средняя латентность бесполезна для SLA: пользователь помнит не средний опыт, а худший. Стандарт практики — p50 (медиана, «типичный» запрос), p95 («один из двадцати») и p99 («один из ста»). Для p99 среднее может отличаться в разы, и именно на p99 живёт гнев пользователей и дежурных инженеров.

Вторая ошибка — измерение на ровной (closed-loop, постоянная частота) нагрузке и перенос выводов на реальную. Реальный трафик bursty: запросы приходят пачками, очередь растёт скачком, и p99 при bursty-нагрузке может быть в пять–десять раз выше p99 при ровном потоке той же средней интенсивности. Если статья меряет latency на нагрузке «10 запросов в секунду, равномерно» — она меряет не то, что будет в проде. Хороший бенчмарк либо воспроизводит реальный trace (лог прод-трафика с его всплесками), либо честно использует синтетический bursty-профиль (например, пуассоновский поток с коэффициентом вариации >> 1) и говорит об этом.

Третья ошибка — сообщать throughput и latency как независимые цифры. Они связаны: максимум throughput достигается на батчах, где p99-латентность уже неприемлема. Правильный способ сообщать оба — Парето-фронтир: кривая «какой максимальный throughput достижим при заданном p99 ≤ X». Disaggregation-системы (PD-разделение, которое мы обсуждали в модулях 10–11; свежий пример — PrfaaS) как раз и улучшают точку на этом фронтире: чуть ниже максимального throughput, радикально лучше p99. Статья, которая сообщает «throughput вырос на 54%» без указания, что случилось с латентностью на этой точке, скрывает половину результата — возможно, p99 вырос вдвое, и для интерактивного сервиса это регресс.

16.3 Препринт-гигиена: читать arXiv как инженер, а не как фанат

Большинство системных статей курса — препринты. Это не приговор, но это факт, который надо учитывать. Peer-review в MLSys/OSDI/NSDI хотя бы гарантирует, что три человека с доступом к вычислениям попытались найти дыры в методологии. Препринт — это авторы, сами проверяющие себя.

Практическая гигиена чтения препринта, по убывающей важности:

  1. Возраст и версия. Препринт v1 от трёх недель назад ещё не прошёл даже неформальную проверку сообществом. Если за полгода вышла v3 с другими числами — это сигнал (что поменялось? баг в бенчмарке? другой базлайн?).
  2. Цитирования и обсуждение. Ноль цитирований — не значит «плохая», но значит «никто ещё не попробовал воспроизвести». Ищите независимые реплики: issues в open-source репозиториях, обсуждения, повторные замеры. Если метод заявлен прорывным, а независимых воспроизведений через полгода нет — это сама по себе информация.
  3. Кто мерил базлайн. Классический источник завышений: базлайн авторов не тюнингован, а их система — тюнингована. Признак аккуратности: таблица, где базлайн показывает цифры, совпадающие с официальными отчётами других групп.
  4. Есть ли код и конфиги. «Код будет выложен позже» в препринте — статистически означает «никогда». Воспроизводимая статья даёт не только код, но и точные конфиги запуска: seed'ы, версии библиотек, спецификацию железа (модель GPU, объём памяти, версии драйверов).
  5. Разброс внутри статьи. SwaT с OOD-accuracy, гуляющей от 31.4% до 99% по парам моделей, — это пример статьи, где разброс виден прямо в таблицах, и это честно. Если вы видите только mean — перечитайте оригинальные таблицы или считайте цифру неподтверждённой.
  6. Совпадение интереса. Вендорский препринт, рекламирующий собственную архитектуру на собственном железе (PrfaaS с внутренним 1T-моделью), автоматически требует большего скепсиса, чем замер академической группы на публичных моделях. Это не цензура — это сдвиг априорной вероятности.

Та же гигиена — в обе стороны. Когда публикуете свой замер, применяйте чек-лист к себе: если ваш отчёт не прошёл бы ваш же чек-лист, он не готов.

16.4 Микробенчмарки: пять способов измерить не то

Микробенчмарк — замер одной операции (matmul, attention-ядра, копирование KV-блока). Прост на вид, но у него есть минимум пять стандартных ловушек.

Ловушка 1: нет прогрева. Первый запуск операции медленный по причинам, не имеющим отношения к стационарной производительности: аллокация памяти, подгрузка кода ядра с диска, адаптация CPU-губернатора, прогрев кэшей BLAS (OpenBLAS и MKL в первый вызов dgemm тратят время на выбор ядра и распределение буферов). Если включить первый прогон в среднее — цифра занижена на десятки процентов. Стандарт: 3–5 разогревочных прогонов, которые не попадают в статистику.

Ловушка 2: нет привязки к ядру. Планировщик ОС мигрирует поток между ядрами, и каждое перелётное ядро холодное: L1/L2 пустые, TLB сброшен, у некоторых ядер другой boost. На ноутбуке с гибридной архитектурой (P- и E-ядра) миграция на E-ядро — это падение в разы. На Windows (наша платформа в этом курсе) дополнительно вносит шум Defender и фоновые службы. Стандарт: taskset на Linux или start /affinity на Windows — привязать бенчмарк к конкретному ядру, исключив миграцию.

Ловушка 3: шум таймера и слишком короткие интервалы. time.perf_counter() на Windows имеет разрешение порядка микросекунд, но реальная погрешность измерения интервала короче ~100 мкс велика: интервал сопоставим с квантом планировщика и переключением контекста. Стандарт: каждая измеряемая итерация должна длиться хотя бы единицы миллисекунд; если сама операция короче — запускайте её пачкой внутри одного интервала и делите.

Ловушка 4: один прогон. Замер «запустил один раз, записал число» имеет неизвестную погрешность, которая на шумной рабочей машине может превышать измеряемый эффект. Эффект, который вы ищете (ускорение 10%), легко утонет в 20% шума одного прогона. Стандарт: N ≥ 20–30 прогонов, из них берётся распределение, а не одно число.

Ловушка 5: неверная статистика по N прогонам. Распределение времени на машине — не нормальное: справа длинный хвост (случайные вытеснения, GC, прерывания). Отсюда два следствия. Среднее арифметическое сдвинуто хвостом и менее устойчиво, чем медиана. И доверительный интервал по формуле «mean ± 1.96·σ/√N» для такого распределения занижен. Практически надёжный вариант — bootstrap: сэмплируйте из ваших N замеров с возвращением много раз, пересчитывайте медиану, берите 2.5-й и 97.5-й перцентили получившегося распределения. Дополнительный приём: из финальной статистики можно выбрасывать явные выбросы (например, значения > p99 × 3), но только если вы честно зафиксировали это правило до замера, а не после, когда оно удобно.

Отдельно — проконтроль корректности. Бенчмарк, который считает неправильно, быстрее любого правильного: выключенный выход, съеденный short-circuit'ом, пустой attention, декодинг в EOS с первого шага. Каждая измеряемая конфигурация должна проверять выходной тензор (контрольную сумму или сравнение с эталоном) до записи числа. В терминологии acceptance-методологии из модуля 10 — это тот же принцип: счётчик принятых токенов обязателен, потому что ускорение без проверки эквивалентности вывода может быть ускорением поломки. Обобщение на любую оптимизацию: correct/min — правильных ответов в минуту, а не токенов в минуту. Через это правило прошли все системы курса: спекулятивный декодинг (выход должен совпадать побитово), квантование (допустимое падение качества задано явно), батчинг (PagedAttention не должен терять блоки).

Индустриальный ориентир: MLPerf Inference v6.1

MLPerf Inference v6.1 (сентябрь 2026) остаётся единственным индустриальным бенчмарком уровня «стандарт»: новые инференс-харнесы такого масштаба не появились. Два сюжета этого раунда:

  • Дебют Vera Rubin NVL72: до 3.7× throughput против GB300 NVL72 на DeepSeek-R1 и Qwen3-VL preview (результаты вендоров в рамках правил MLPerf — не независимое воспроизведение). Очередная смена поколения железа за год; следите, чтобы сравнения в статьях были внутри одного раунда.
  • Сдвиг тестов к RAG- и агентным профилям — бенчмарк догоняет реальный прод-трафик, где интерактивные многошаговые сценарии с обращением к контексту вытесняют чистый генеративный поток.

Методологический образец: 4 уровня наблюдения

Препринт по транспорту KV в PD-дизагрегации (тот самый, с 268 прогонами на MiniMax-M3, разобранный в модулях 10 и 13) даёт редкий для препринтов образец глубины наблюдения: 4 уровня — L0/L0b/L1/L2-L3 — от сырых счётчиков до причинного разбора отказов, и 268 прогонов, на которых статистика устойчива. Это конкретный ответ на вопрос «сколько мерить, чтобы утверждать»: не «погонял три раза», а зафиксированная лестница уровней + сотни прогонов. Берите как шаблон для собственных замеров транспортных и PD-конфигураций.

Задание 16.1: переписать бенчмарк модуля 01 по-взрослому

В модуле 01 _time(..., repeat=1) давал одно число за прогон — и на этом числе был построен вывод про 172x. Теперь сделаем методологически правильно и посмотрим, что изменится.

Напишите mod16_bench.py, который:

  1. Замеряет тот же BLAS matmul (n=1024, float64) с полной методологией: привязка к ядру, 5 разогревочных прогонов, затем N=30 измерительных прогонов; на каждом — минимум единицы миллисекунд работы.
  2. Считает по 30 замерам: медиану, p95, p99, среднее, bootstrap-ДИ 95% для медианы.
  3. Сравнивает «одно число из одного прогона» (первый измерительный прогон) с итоговой медианой и оценивает, попадает ли одиночное измерение в доверительный интервал.
  4. Проверяет корректность каждого прогона: max|C − C_etalon| < 1e-10, иначе бенчмарк падает, а не пишет число.

Задание 16.2: чек-лист чтения препринта

Возьмите три статьи курса (минимум одну из: SwaT, PrfaaS, Cross-Model KV Transfer) и заполните таблицу: дата препринта, число цитирований/реплик, есть ли код, кто мерил базлайн, показан ли разброс (не только mean), совпадение интереса авторов с результатом. По каждой строке — вердикт «переносимо на мою систему / переносимо с оговорками / только направление эффекта». Сохраните таблицу как шаблон: вы будете применять её к каждой новой статье, прежде чем взять из неё цифру в рабочий отчёт.

Код

Полный самодостаточный файл mod16_bench.py. Зависимости: numpy (torch не нужен — эталон считаем самой numpy, нам важна не реализация, а методология замера).

"""
Модуль 16. Методологически корректный микробенчмарк matmul на CPU.
Запуск: python mod16_bench.py
Зависимости: numpy.

Пять правил, которые здесь реализованы:
  1. прогрев (5 прогонов вне статистики)
  2. привязка к ядру (best-effort: Windows / Linux)
  3. интервал измерения >> шум таймера (batched runs)
  4. N=30 прогонов, распределение, а не число
  5. bootstrap-ДИ + проверка корректности каждого прогона
"""
from __future__ import annotations

import os
import platform
import subprocess
import sys
import time

import numpy as np

RNG = np.random.default_rng(0)

N_DIM = 1024          # размер матрицы, как в модуле 01
WARMUP = 5            # разогревочных прогонов
N_RUNS = 30           # измерительных прогонов
INNER = 5             # matmul'ов внутри одного измерения (таймеру нужен >= 1-5 мс)
CHECK_RTOL = 1e-12    # допуск корректности для float64


def pin_to_core() -> str:
    """Best-effort привязка текущего процесса к одному ядру.

    Windows: PowerShell -> Process.Id -> ProcessorAffinity.
    Linux:   taskset -pc <pid> 0 (первый CPU).
    Не критично для корректности, но резко снижает разброс.
    """
    try:
        if platform.system() == "Windows":
            # affinity = битовая маска; 1 = только CPU 0
            subprocess.run(
                ["powershell", "-NoProfile", "-Command",
                 f"`USD p = Get-Process -Id {os.getpid()}; "
                 f"`USD p.ProcessorAffinity = [IntPtr]1"],
                check=True, capture_output=True, timeout=15)
            return "Windows: affinity -> CPU 0"
        subprocess.run(["taskset", "-pc", str(os.getpid()), "0"],
                       check=True, capture_output=True, timeout=15)
        return "Linux: taskset -> CPU 0"
    except Exception as e:  # в контейнере/без прав может не выйти — это ок
        return f"pin failed (продолжаем без привязки): {e}"


def bench_once(matmul_fn, A, B, inner: int, etalon: np.ndarray) -> float:
    """Одно измерение: inner matmul'ов подряд, среднее время одного.

    Возвращает время ОДНОГО matmul в секундах. Падает, если результат
    не совпал с эталоном — бенчмарк, который считает неправильно,
    быстрее правильного, и он не имеет права молча писать число.
    """
    t0 = time.perf_counter()
    out = None
    for _ in range(inner):
        out = matmul_fn(A, B)
    dt = (time.perf_counter() - t0) / inner
    if not np.allclose(out, etalon, rtol=CHECK_RTOL, atol=CHECK_RTOL):
        raise RuntimeError(
            f"корректность нарушена: max|C-C_etalon|="
            f"{np.abs(out - etalon).max():.3e}")
    return dt


def bootstrap_ci(samples: np.ndarray, stat=np.median,
                 n_boot: int = 10_000, seed: int = 1) -> tuple[float, float]:
    """95% ДИ для statistic по bootstrap (percentile method).

    Для skewed распределений (а время на CPU скошено вправо) это
    надёжнее нормальной аппроксимации mean ± 1.96*sigma/sqrt(N).
    """
    rng = np.random.default_rng(seed)
    idx = rng.integers(0, len(samples), size=(n_boot, len(samples)))
    stats = stat(samples[idx], axis=1)
    return (float(np.percentile(stats, 2.5)),
            float(np.percentile(stats, 97.5)))


def report(name: str, samples_ms: np.ndarray) -> None:
    lo, hi = bootstrap_ci(samples_ms)
    single = samples_ms[0]  # «методология модуля 01»: один прогон
    inside = lo <= single <= hi
    print(f"\n=== {name} ===")
    print(f"прогонов: {len(samples_ms)} (прогрев {WARMUP}, "
          f"inner {INNER} matmul/замер)")
    print(f"медиана : {np.median(samples_ms):8.3f} мс  "
          f"[{lo:8.3f}, {hi:8.3f}] (95% bootstrap ДИ)")
    print(f"среднее : {np.mean(samples_ms):8.3f} мс   "
          f"p95: {np.percentile(samples_ms, 95):8.3f}   "
          f"p99: {np.percentile(samples_ms, 99):8.3f} мс")
    print(f"разброс : min {samples_ms.min():.3f} / max {samples_ms.max():.3f} мс "
          f"(max/min = {samples_ms.max() / samples_ms.min():.2f})")
    print(f"одиночный прогон (как в модуле 01): {single:.3f} мс -> "
          f"{'попал в ДИ' if inside else 'ВНЕ ДИ: одиночный замер смещён'}")
    gflops = 2 * N_DIM ** 3 / (np.median(samples_ms) / 1e3) / 1e9
    print(f"производительность по медиане: {gflops:6.2f} GFLOP/s")


def main() -> None:
    print(f"платформа: {platform.system()} {platform.release()}, "
          f"python {sys.version.split()[0]}, numpy {np.__version__}")
    print(f"привязка к ядру: {pin_to_core()}")

    A = RNG.standard_normal((N_DIM, N_DIM))
    B = RNG.standard_normal((N_DIM, N_DIM))
    etalon = A @ B

    # --- прогрев: 5 прогонов, в статистику не попадают ---
    for _ in range(WARMUP):
        A @ B

    # --- N измерительных прогонов ---
    blas_ms = np.array(
        [bench_once(np.matmul, A, B, INNER, etalon) for _ in range(N_RUNS)]
    ) * 1e3
    report("numpy BLAS (float64, n=1024)", blas_ms)

    # --- блочная numpy-реализация из модуля 01 под той же методологией ---
    def blocked(A, B, block=64):
        A = np.asarray(A, dtype=np.float64)
        B = np.asarray(B, dtype=np.float64)
        m, k = A.shape
        n = B.shape[1]
        C = np.zeros((m, n), dtype=np.float64)
        for k0 in range(0, k, block):
            for j0 in range(0, n, block):
                acc = C[:, j0:j0 + block]
                for p in range(k0, min(k0 + block, k)):
                    acc += np.outer(A[:, p], B[p, j0:j0 + block])
        return C

    for _ in range(WARMUP):
        blocked(A, B)
    blk_ms = np.array(
        [bench_once(blocked, A, B, 1, etalon) for _ in range(N_RUNS)]
    ) * 1e3
    report("blocked numpy (float64, n=1024, block=64)", blk_ms)

    # --- сравнение двух конфигураций как сравнение распределений ---
    speed = np.median(blk_ms) / np.median(blas_ms)
    lo_b, hi_b = bootstrap_ci(blas_ms)
    print(f"\n=== сравнение конфигураций ===")
    print(f"blocked / BLAS по медианам: {speed:.2f}x")
    ok = hi_b < np.median(blk_ms)
    print(f"ДИ BLAS [{lo_b:.3f}, {hi_b:.3f}] против blocked "
          f"{np.median(blk_ms):.3f} мс -> "
          f"{'различие статистически значимо' if ok else 'различие НЕ значимо: интервалы перекрываются'}")
    print("\nвывод: если разница между конфигурациями меньше ширины ДИ — "
          "эффект нельзя заявлять, сколько бы нулей ни было после запятой "
          "в одиночном замере.")


if __name__ == "__main__":
    main()

Как проверить

  1. python mod16_bench.py отрабатывает без исключений; оба блока отчёта напечатаны, ни один прогон не упал по корректности.
  2. Медиана BLAS на вашем CPU стабильна между повторными запусками скрипта: при двух запусках подряд медианы отличаются не больше, чем на ширину доверительного интервала.
  3. Умышленно сломайте корректность (замените etalon = A @ B на etalon = np.zeros_like(A)) — бенчмарк должен упасть с RuntimeError на первом же прогоне, а не напечатать число. Это проверка, что correct-гейт работает.
  4. Уберите прогрев (WARMUP = 0) и запустите: медиана, скорее всего, уедет вверх, а p99 — особенно. Расхождение с основной версией — ваша наглядная оценка цены пренебрежения прогревом.
  5. По заданию 16.2: таблица из трёх статей заполнена, у каждой строки есть вердикт переноса.

Прогон верификатора (Windows 11, python 3.14.9, numpy 2.4.3, привязка к CPU 0): BLAS медиана 643.8 мс [484.0, 1745.4], max/min по 30 прогонам — 4.56; одиночный BLAS-прогон 1747.6 мс — вне доверительного интервала; blocked / BLAS по медианам 2.67x, но интервалы перекрываются — различие статистически не значимо. Итог ровно тот, которого требует методология: на шумной рабочей машине одиночный замер и «эффект в 2.7 раза» одинаково недостоверны.

Сводка

  • Числа курса делятся на три класса: арифметика (переносится), эмпирика на вашей машине (переносится с оговорками), авторские headline (не переносятся без независимого воспроизведения). Imprint 19с→0.3с не воспроизведён; PrfaaS +54% — замер автора на внутреннем 1T-модели; DFlash 0.25x под нагрузкой на 4060 показывает, что даже знак ускорения зависит от условий.
  • Говорите на языке TTFT/TPOT/p50/p95/p99 и Парето-фронтира throughput–latency. Средние по ровной нагрузке — не описание прода; bursty-трафик поднимает p99 в разы.
  • Препринт ≠ проверено: возраст, цитирования, наличие кода, кто мерил базлайн, показан ли разброс (SwaT 31.4%–99% — пример честного разброса; Cross-Model KV «две пары деградируют резко» — пример непереносимости средних).
  • Headline-дивергенция UBTree: 6.94× (сайт, Qwen3-8B), 5.84–6.94× (аннотация, Qwen3-4B/8B) и 3.428× E2E (model card, Ling3-Flash) — три несопоставимых конфигурационных режима с разными target, бюджетами дерева и наборами бенчмарков; переносить «6.94×» без контекста нельзя.
  • Микробенчмарк без методологии измеряет шум: прогрев, привязка к ядру, длинные интервалы, N≥30, bootstrap-ДИ, correct-гейт на каждом прогоне. correct/min — главная метрика любой оптимизации, acceptance-счётчики обязательны.
  • Индустриальный ориентир — MLPerf Inference v6.1 (сент. 2026): дебют Vera Rubin NVL72 с до 3.7× throughput против GB300 NVL72, сдвиг тестов к RAG/агентам; вендорские результаты внутри правил MLPerf, не независимые.
  • Образец глубины наблюдения: 4-уровневая методология L0/L0b/L1/L2-L3 на 268 прогонах (KV-транспорт в PD) — лестница уровней плюс сотни прогонов, а не три «погонял».
  • Переписанный бенчмарк модуля 01 показал расхождение одиночного замера с медианой распределения — и это типовое, а не исключение.


Навигация по курсу: Хаб курса · Предыдущий: Самоспекулятивный декодинг: драфтер внутри модели · Следующий: Unified memory и гибридные ноды: четвёртый путь инференса