График пропускной способности — это не обещание
Один и тот же сервис инференса можно описать двумя честными способами. Инженер показывает кривую: столько-то токенов в секунду на насыщении, выше конкурента, ускорение за квартал двукратное. Продукт показывает другое: доля диалогов, где ответ начинался дольше трёх секунд, выросла вдвое. Оба измерения верны, и они почти не связаны между собой.
Разница не в аккуратности замера, а в том, что измеряется. описывает, сколько работы машина успевает сделать, когда её загрузили полностью. Продукт живёт не средним, а хвостом: его интересует, что случилось с теми запросами, которым не повезло оказаться в очереди за чужим длинным контекстом. Бенчмарк без порога описывает железо; обещание описывает поведение сервиса на границе.
Формат такого обещания придумывать не нужно — индустрия его уже записала. MLPerf Inference в версии 5.1 оценивает систему на модели Llama 3.1-8B не абстрактной скоростью, а максимальной устойчивой нагрузкой внутри двух границ. В сценарии Server это TTFT не выше 2 секунд и TPOT не выше 100 миллисекунд. В добавленном сценарии Interactive границы жёстче: 0,5 секунды и 30 миллисекунд.
MLCommons сама переводит эти пороги в человеческую величину: 100 миллисекунд на токен — это примерно 480 слов в минуту, заметно быстрее обычного чтения, а 30 миллисекунд — около 1600 слов в минуту. Второй сценарий появился ради случаев, где важна отзывчивость: помощники в коде и инструменты, работающие в реальном времени.
К октябрю 2026 года формат закрепился. В раунде MLPerf Inference v6.1 от 16 сентября 2026 года своя пара порогов задана для каждой языковой модели: у Llama 2 70B в интерактивном сценарии это 450 миллисекунд до первого токена и 40 миллисекунд на токен, у DeepSeek-R1 — 1,5 секунды и 15 миллисекунд. Рассуждающей модели разрешают дольше думать до начала ответа, но требуют быстрее печатать: пороги задаются под сценарий, а не под модель вообще. Там же MLCommons объявила, что для датацентров MLPerf Inference уступит место MLPerf Endpoints — бенчмарку, который нагружает систему через API, то есть так, как её видит клиент.
Исследователи дали такой мере собственное имя — . В DistServe (OSDI 2024) она определена как максимальная интенсивность запросов, при которой заданная доля запросов — например, 90 % — укладывается в SLO, в пересчёте на один ускоритель. В вводном примере статьи система, совмещающая обе стадии на одном ускорителе, держит внутри порогов около 1,6 запроса в секунду, а система с разделёнными стадиями — 3,3 запроса на ускоритель. Модель и железо те же; различается доля работы, которая уложилась в обещание.
Почему у темпа генерации вообще есть порог, объясняет простое наблюдение: текст быстрее чтения не делает ответ лучше. Авторы препринта Andes принимают скорость чтения за 4,8 токена в секунду, а прослушивания — за 3,3 и считают качество ответа по тому, насколько выдача отстаёт от этого темпа. Это препринт, а не стандарт, но логика полезна: обгонять читателя бессмысленно, отставать — заметно. Значит, после некоторой границы ускорение генерации ничего не даёт продукту, а время до первого токена даёт всегда.
обещание = порог на старте ответа + порог на темпе генерации + доля запросов, которая в них укладывается
Дальше вся статья — про то, откуда берётся нарушение этого обещания. Сначала разберём, почему одной кривой задержки недостаточно, затем — где на самом деле живёт хвост, чем измеряется ёмкость, как всё это ломается и что делать вместо отказа. Устройство самого сервера — как он собирает пакеты, где лежит KV-кеш и зачем разделять стадии — подробно разобрано в статье об инференсе как распределённой системе; здесь оно нужно только там, где влияет на обещание.
Одной кривой задержки не хватает: величин три
«Медленно» для сервиса генерации распадается минимум на три разных события. Первое: пользователь нажал кнопку и ничего не происходит — это время до первого токена. Второе: ответ пошёл, но печатается медленнее, чем читается — это темп генерации, время на выходной токен. Третье: ответ целиком не успел прийти до таймаута вызывающей системы — это сквозная задержка.
| Величина | Что измеряет | От чего зависит | Как выглядит для пользователя |
|---|---|---|---|
| TTFT | Сколько ждали до первого токена | Очередь, планировщик, обработка входа | Пользователь думает, что кнопка не сработала |
| TPOT / межтокенная задержка | Как быстро идёт текст после старта | Размер батча, соседи по машине, длина контекста | Ответ печатается медленнее, чем читается |
| Сквозная задержка | Сколько занял весь ответ | Сумма первых двух и длина ответа | Сценарий не укладывается в таймаут вызывающего |
| Время в очереди | Сколько запрос ждал до начала работы | Загрузка, приоритеты, вытеснения | Ничего не видно на графиках модели — и это главный признак |
Складывать эти величины в одну кривую — потеря информации, а не упрощение. Время до первого токена растёт от очереди и от длины входа; темп генерации падает, когда на машине стало больше соседей; сквозная задержка может вырасти просто потому, что ответы стали длиннее. Три разные причины, три разные ручки.
vLLM уже разделяет задержки и состояние очереди. Имена ниже связывают метрику с вопросом, который нужно задать сервису.
| Вопрос | Метрика |
|---|---|
| Ожидание первого токена | vllm:time_to_first_token_seconds |
| Задержка между токенами | vllm:inter_token_latency_seconds |
| Время всего ответа | vllm:e2e_request_latency_seconds |
| Время ожидания в очереди | vllm:request_queue_time_seconds |
| Число выполняемых запросов | vllm:num_requests_running |
| Число ожидающих запросов | vllm:num_requests_waiting |
vllm:time_to_first_token_secondsvllm:inter_token_latency_secondsvllm:e2e_request_latency_secondsvllm:request_queue_time_secondsvllm:num_requests_runningvllm:num_requests_waitingОтдельная метрика на время в очереди — не мелочь. Она отделяет то, за что отвечает модель, от того, за что отвечает планировщик и ёмкость. Если на дашборде одна кривая задержки, это решение команды, а не ограничение инструмента: разделение уже сделано за вас.
Хвост живёт в очереди, а не в модели
Инженерная интуиция подсказывает, что задержка растёт, когда не хватает мощности. Это верно лишь наполовину, и вторая половина объясняет большинство неприятных инцидентов.
Классическое приближение для среднего ожидания в очереди — формула Кингмана, опубликованная в 1961 году. Она раскладывает ожидание в произведение трёх множителей: загрузки, вариабельности и времени обслуживания.
ожидание ≈ [ρ / (1 − ρ)] × [(c²прихода + c²обслуживания) / 2] × среднее время обслуживания
Управленческий смысл формулы в том, что ожидание растёт по трём независимым причинам: приход стал более рваным, обслуживание стало более разбросанным, загрузка приблизилась к единице. Докупка мощности работает только с третьим множителем.
У инференса первые два множителя экстремальны, и это его отличает от обычного сервиса. На одном и том же адресе живут запрос с контекстом в несколько сотен токенов и запрос с контекстом в сотни тысяч; ответ бывает в одну строку и на десять экранов. Разброс времени обслуживания измеряется не процентами, а порядками. Поэтому кластер, у которого «загрузка всего 60 процентов», уверенно выдаёт неприемлемый хвост — и добавленный ускоритель этот хвост почти не двигает.
Это видно и в реальных трейсах. Авторы Splitwise (ISCA 2024) опубликовали трейсы двух сервисов Azure: у помощника в коде медианный вход — 1500 токенов, а медианный ответ — 13; у диалогового сервиса — 1020 и 129 токенов, причём длина ответа распределена почти бимодально. Набор BurstGPT (KDD 2025) — 213 дней трейсов региональных сервисов Azure OpenAI — показывает второй множитель с другой стороны: всплески прихода резко меняют характер в рабочие часы, а доля отказов диалогового сервиса на модели ChatGPT в этих трейсах стабильно выше 5 %. Авторы связывают такие всплески отказов с нехваткой памяти, к которой мы вернёмся в следующем разделе.
Граница применимости. Формула Кингмана — приближение для одноканальной очереди, и точнее всего она работает вблизи насыщения. Сервер инференса — не одноканальная очередь: он собирает запросы в пакеты, вытесняет их и перепланирует на каждой итерации. Здесь эта формула нужна как инструмент мышления, а не как калькулятор ёмкости.
Учебный расчёт: бюджет хвоста задержки
Обещание из первого раздела можно разложить на бюджет, а бюджет — превратить в порог тревоги. Пусть продукт обещает, что 95 % интерактивных запросов увидят первый токен не позже чем через 2 секунды. Числа ниже условные; у вас они берутся из собственных замеров.
| Этап | Бюджет | Откуда число |
|---|---|---|
| Шлюз, авторизация, маршрутизация | 0,1 с | Замер на входе сервиса |
| Обработка входа типичной длины | 0,4 с | 95-й процентиль на вашей модели и железе |
| Запас на разброс и сеть до клиента | 0,3 с | Решение команды |
| Ожидание в очереди | 1,2 с | Остаток: 2,0 − 0,1 − 0,4 − 0,3 |
Чтобы перевести 1,2 секунды в число, которое видно на дашборде, нужен закон Литтла: среднее число ожидающих равно интенсивности прихода, умноженной на среднее время ожидания (L = λW). Если на реплику приходит 20 запросов в секунду, то при среднем ожидании 1,2 секунды в очереди в среднем стоят 24 запроса. Порог тревоги по vllm:num_requests_waiting должен быть заметно ниже 24: закон Литтла связывает средние, а обещание дано про 95-й процентиль, и при рваном приходе хвост ожидания намного длиннее среднего.
Тот же бюджет можно выразить в токенах. Если реплика обрабатывает, скажем, 20 тысяч входных токенов в секунду, то в 1,2 секунды очереди помещается не больше 24 тысяч необработанных входных токенов. В актуальных версиях vLLM есть настройка ровно для этого: max_num_queued_tokens ограничивает суммарный вход запросов, ещё ожидающих обработки, и отвечает 503 сверх лимита. Документация планировщика прямо называет её механизмом качества по TTFT и предлагает выставлять как целевое TTFT, умноженное на скорость обработки входа.
Хвост складывается по цепочке вызовов
Последний множитель хвоста появляется у агентов. В статье The Tail at Scale (Dean, Barroso, CACM 2013) показано, что если каждый сервер медленно отвечает в одном случае из ста, то запрос, ждущий ответа ста серверов, медленный в 63 % случаев. У агента та же арифметика растянута во времени: сессия из 10 последовательных вызовов модели, каждый из которых с вероятностью 1 % выходит за порог, хотя бы раз выйдет за него с вероятностью 1 − 0,99¹⁰ ≈ 9,6 %; при 20 вызовах — около 18 %. Обещание на уровне одного вызова не равно обещанию на уровне задачи, и для агентских сценариев его нужно пересчитывать.
Ёмкость измеряется памятью, а не числом запросов
Второй источник сюрпризов — единица ёмкости. В обычном сервисе она интуитивна: столько-то одновременных запросов, столько-то потоков. В инференсе ёмкость определяется тем, помещаются ли рядом всех активных запросов. Единица ёмкости — память под контексты, а не число соединений.
Насколько дорога эта память, показала статья о PagedAttention (SOSP 2023): в системах того времени на реальные состояния токенов уходило лишь 20,4–38,2 % памяти KV-кеша, остальное съедали резерв под максимальную длину и фрагментация. Страничная раскладка памяти подняла пропускную способность в 2–4 раза при той же задержке и стала основой vLLM. Статью подробно разбирает выпуск Research Insights №32.
Учебный расчёт: сколько диалогов помещается в узел
Объём KV-кеша на токен считается по архитектуре модели: два тензора (ключи и значения) на каждый слой, на каждую KV-голову, по размеру головы, умноженные на байты на число.
байт на токен = 2 × слои × KV-головы × размер головы × байт на число
У Llama 3.1 70B, по описанию архитектуры в репозитории Meta, 80 слоёв, 8 KV-голов и размер головы 128 (8192 ÷ 64). В BF16 это 2 × 80 × 8 × 128 × 2 = 327 680 байт, то есть 320 КиБ на каждый токен контекста. Дальше — учебный узел из четырёх ускорителей H100 по 80 ГБ с тензорным параллелизмом:
- движку по умолчанию отдают 92 % памяти — в актуальной документации vLLM это значение gpu-memory-utilization; от 320 ГБ остаётся около 294 ГБ;
- веса в BF16 занимают около 140 ГБ: 70 млрд параметров по 2 байта;
- на активации и графы вычислений заложим условные 10 ГБ;
- на KV-кеш остаётся около 144 ГБ, то есть примерно 440 тысяч токенов.
Диалог с входом 2,5 тысячи токенов и ответом 0,5 тысячи занимает 3 тысячи токенов, и таких диалогов в узел одновременно помещается около 145. Одна агентская сессия с контекстом 128 тысяч токенов занимает 29 % пула — столько же, сколько 43 диалога. Перевод KV-кеша в FP8 вдвое уменьшает байты на токен и примерно удваивает этот запас. Расчёт приблизительный: движок выделяет память блоками, делит общие префиксы и резервирует место под генерацию. Сверять его стоит со строками, которые vLLM печатает при старте: GPU KV cache size: N tokens и Maximum concurrency for X tokens per request. Документация советует добавлять ускорители, если эти числа ниже требуемых.
Отсюда следствие, которое ломает интуицию: один запрос с очень длинным контекстом стоит дороже десятков коротких диалогов. И именно он вытесняет соседей. Когда памяти перестаёт хватать, vLLM пишет в лог честную фразу: Sequence group N is preempted by PreemptionMode.RECOMPUTE mode because there is not enough KV cache space.
Вытеснение — механизм устойчивости, а не поломка: система предпочитает снять часть работы, чтобы остальные продолжали. Но у него есть цена. Вытесненный запрос затем считается заново, и его сквозная задержка подскакивает. В версии V1 режимом по умолчанию выбран именно пересчёт, а не выгрузка состояния: в этой архитектуре пересчитать дешевле, чем перекладывать.
Это настройки из документации, а не универсальный рецепт. Команда выбирает их по своему ограничению и проверяет эффект на той же нагрузке. Распределение вычислений соответствует тензорному параллелизму, разделение этапов — конвейерному.
| Если ограничение здесь | Что изменить | Что проверить после изменения |
|---|---|---|
| Память под контекст | Увеличить долю памяти, доступную движку | Помещаются ли нужные контексты и хватает ли памяти остальным задачам |
| Одновременно выполняемые запросы | Уменьшить предельное число последовательностей | Очередь, время первого токена и число принятых запросов |
| Работа в одном пакете | Изменить предельное число токенов в пакете | Время первого токена и темп генерации: эффект различается |
| Модель на устройствах | Распределить модель между большим числом ускорителей: разделить вычисления или этапы обработки | Задержки, память и стоимость добавленных ускорителей |
У SGLang те же ручки называются иначе: --max-running-requests, --chunked-prefill-size и --schedule-conservativeness — чем больше значение, тем осторожнее планировщик принимает новые запросы. Показательна страница тюнинга SGLang: хорошей загрузкой она называет занятость KV-кеша выше 0,9 и советует держать очередь в сотни запросов. Но это раздел о максимальной пропускной способности пакетной офлайн-обработки. Для интерактивного обещания такая очередь означает секунды до первого токена. Одна и та же метрика хороша или плоха в зависимости от того, что обещано.
Обе системы умеют приоритеты. В vLLM это --scheduling-policy priority: меньшее значение обслуживается раньше. В SGLang — --enable-priority-scheduling, где по умолчанию раньше идут большие значения, а направление переключается отдельным флагом. Шкалы противоположны, и это стоит записать в контракт с клиентами, иначе после смены движка пакетная задача окажется впереди интерактивной.
Ручка, которая явно переключает между двумя обещаниями
Отдельно стоит выделить предельное число токенов в пакете. В V1 по умолчанию включено смешивание с генерацией в одном пакете, и этот параметр прямо распределяет машину между двумя обещаниями из первого раздела: меньшие значения улучшают темп генерации, потому что обработка входа реже прерывает генерацию соседей; большие значения улучшают время до первого токена.
Откуда взялся этот компромисс, показывает Sarathi-Serve (OSDI 2024). Планировщики, которые ставят обработку входа вперёд, создают остановки генерации: в vLLM того времени такая остановка длилась до нескольких секунд. Нарезка входа на части позволяет добавлять новые запросы в пакет, не останавливая генерацию соседей, и при ограничении на 99-й процентиль межтокенной задержки дала до 2,6 раза больше ёмкости для Mistral-7B на одном A100.
Это редкий случай, когда компромисс не спрятан в эвристике, а вынесен в конфигурацию одним числом. Значит, и решение здесь продуктовое: какой из двух порогов вам дороже нарушить.
Если одной ручки не хватает, следующий шаг — : обработку входа и генерацию ставят на разные пулы ускорителей и масштабируют по отдельности. В DistServe это дало до 7,4 раза больше запросов или в 12,6 раза более жёсткий SLO при доле запросов внутри порогов выше 90 %. В llm-d 1.0 от 20 октября 2026 года пулы двух стадий управляются как одна логическая единица — для согласованных выкаток и обновлений. Цена решения — передача KV-кеша между пулами и вторая модель ёмкости; где граница доказанного выигрыша, разбирает фронтир инференса LLM.
Четыре способа сломаться под нагрузкой
Отказы инференс-сервиса удобно свести к небольшой таксономии. Она полезна тем, что у каждой строки свой ранний признак — и он виден раньше, чем первая жалоба.
| Отказ | Причина | Что происходит | Ранний признак |
|---|---|---|---|
| Вытеснение по KV-памяти | Контексты не помещаются целиком | Пересчёт вытесненного запроса, скачок сквозной задержки | `vllm:num_preemptions`, `vllm:kv_cache_usage_perc` |
| Накопленная очередь | Приход выше устойчивой ёмкости | Память под ожидающие запросы, рост TTFT без роста нагрузки на GPU | `vllm:num_requests_waiting`, `vllm:request_queue_time_seconds` |
| Усиление ретраями | Клиент повторяет запрос по таймауту | Повторное занятие памяти под весь контекст, каскад | Расхождение числа запросов клиента и сервера |
| Холодный старт реплики | Автомасштабирование добавило под | Минуты до первой полезной работы новой реплики | Время от решения масштабировать до готовности |
Про очередь стоит сказать отдельно, потому что здесь работает старое правило из инженерии надёжности. Очередь — это не бесплатное ожидание: она расходует память и добавляет задержку. В книге Google по эксплуатации приводится простой расчёт: если запрос обрабатывается 100 миллисекунд, а очередь вдесятеро длиннее пула обработчиков, то при полной очереди запрос занимает 1,1 секунды, и почти всё это время он ждёт. Рекомендация — держать очередь короткой относительно пула и отклонять запросы рано, а не копить их.
В vLLM этот совет стал настройкой. Кроме упомянутого лимита на необработанный вход, max_num_queued_reqs ограничивает число запросов в работе и в очереди; сверх лимита сервер отвечает 503, чтобы клиент повторил запрос на другом экземпляре. По умолчанию оба лимита выключены, и очередь vLLM без них не ограничена: короткую очередь нужно включить явно.
Ранний отказ — не только учебная рекомендация. Mooncake, платформа сервиса Kimi от Moonshot AI, описана в статье, получившей награду лучшей работы на FAST 2025. Планировщик заранее отклоняет запросы, которые по прогнозу не уложатся в SLO, чтобы разгрузить кластер. В целом архитектура позволила Kimi обслуживать на 115 % и 107 % больше запросов на кластерах A800 и H800. В расширенной версии статьи авторы прямо называют перегрузку частым состоянием в часы пик и приводят отдельный эксперимент: на записи из 23 тысяч реальных запросов, проигранной с удвоенной скоростью, базовая система отклонила 4183 запроса, ранний отказ — 3771, ранний отказ с прогнозом нагрузки — 3589. Отказов меньше, потому что кластер не тратит работу на запросы, которые всё равно не успеют.
Ретраи заслуживают отдельного абзаца, потому что в инференсе они дороже. В главе Handling Overload книги Google по эксплуатации даже лимит в три попытки на запрос при массовых отказах поднимает нагрузку почти втрое. Поэтому к нему добавляют второй бюджет: клиент повторяет запросы, только пока повторы составляют меньше 10 % его запросов, и тогда рост в общем случае ограничен примерно 1,1 раза. Там же описано адаптивное ограничение на клиенте: клиент сам отбрасывает часть запросов с вероятностью max(0, (запросы − K × принятые) / (запросы + 1)) за последние две минуты, а авторы предпочитают K = 2. Соседняя глава добавляет серверный бюджет — например, не более шестидесяти повторов в минуту на процесс.
Специфика инференса в том, что повтор здесь не просто добавляет запрос в очередь. Он заново занимает память под весь контекст — тот самый ресурс, нехватка которого и вызвала отказ. Ретрай без бюджета в таком сервисе не просто удлиняет очередь: он вытесняет тех, кто уже почти дописал ответ, и превращает деградацию в отказ.
Деградация вместо пятисотки
У обычного сервиса выбор под перегрузкой небогатый: обслужить или отказать. У генеративного сервиса есть третий путь, и он почти всегда лучше: отдать более дешёвый ответ. Между «как обычно» и «ошибка» лежит целая шкала.
- ограничить длину ответа: короче, но вовремя;
- перевести запрос на модель поменьше и честно пометить это в интерфейсе;
- выключить дорогой режим рассуждений там, где он не критичен;
- сузить поиск по документам: меньше найденных фрагментов, быстрее ответ;
- поставить запрос в отложенный контур и вернуть результат позже.
Различие между сбросом нагрузки и деградацией стоит держать в голове явно: первый отбрасывает часть трафика, чтобы сервер выжил, вторая снижает качество работы вместо отказа. И у обоих одна общая проблема: этот код выполняется редко, а значит, к моменту настоящего инцидента он с большой вероятностью не работает. Путь деградации нужно регулярно исполнять — иначе он существует только на схеме.
Второй инструмент — приоритеты. Интерактивный запрос и пакетная обработка не должны стоять в одной очереди на равных, и это уже признано на уровне платформенных стандартов. Расширение Gateway API для инференса в Kubernetes появилось именно потому, что обычная балансировка сюда не подходит: сессии длинные, ресурсоёмкие и частично хранят состояние, а один под держит несколько активных сессий вместе с их кешами в памяти. Маршрутизация по пути запроса или по кругу этого не видит.
Проект вводит понятие пула серверов модели, которым владеет платформа, и отдельный компонент, выбирающий эндпоинт под настроенные цели, — среди сигналов упоминаются состояние префиксного кеша и доступность адаптеров. Анонс опубликован 5 июня 2025 года, пул серверов модели стал стабильным API в версии 1.0 в сентябре 2025-го. За год граница сдвинулась: в версии 1.6 (август 2026) полноценный компонент выбора эндпоинта и объекты с приоритетами переехали в llm-d, а сам проект сосредоточился на стандарте, ресурсах Kubernetes и тестах совместимости.
В llm-d приоритеты и сброс нагрузки собраны в одном механизме. Цель запроса задаёт его приоритет; отрицательное значение помечает запрос как сбрасываемый. Управление потоком раскладывает запросы по очередям приоритетов, ограничивает ёмкость каждой и отвечает 429, когда лимит исчерпан; при необходимости оно может прервать уже отправленный низкоприоритетный запрос ради более важного. В llm-d 1.0 это заявлено как управление потоком с учётом приоритетов и справедливости, но в документации механизм пока включается отдельной настройкой.
Есть и третий путь между вытеснением и отказом — перенести запрос. Llumnix (OSDI 2024) из Alibaba переносит запрос вместе с KV-кешем на другой экземпляр почти без простоя и использует это для балансировки, приоритетов и освобождения узлов. В экспериментах авторов хвост задержки улучшился на порядок, высокоприоритетные запросы ускорились до 1,5 раза, а при сопоставимом хвосте расходы снизились до 36 %. Механизм разобран в разборе Llumnix из серии об инференсе на кластере.
Управление очередью как отдельная задача описано в QLM (SoCC 2024): система держит интерактивные и пакетные запросы с разными SLO в одной очереди и упорядочивает её по оценке времени ожидания каждого запроса; та же оценка управляет вытеснением, балансировкой и сменой моделей. Оценка опирается на требования облачного провайдера. Авторы сообщают о росте доли запросов внутри SLO на 40–90 % и пропускной способности на 20–400 %. Препринт SLOs-Serve идёт дальше и распределяет токены под ограничения нескольких SLO: заявлен рост ёмкости на ускоритель в среднем в 2,2 раза на шести сценариях — от суммаризации до вызова инструментов. На 31 октября 2026 года у препринта по-прежнему одна версия апреля 2025 года и нет подтверждённой площадки, поэтому это сигнал направления, а не ожидаемый выигрыш.
Платформа: чем масштабировать и почему это долго
Автомасштабирование инференса начинается с выбора метрики, и первый соблазн — самый неудачный. Загрузка ускорителя плохо подходит на роль сигнала: она не измеряет, сколько полезной работы делается, пока ускоритель занят, и потому плохо связана и с задержкой, и с пропускной способностью.
Рабочих сигнала два, и они отвечают на разные вопросы. Размер очереди показывает, что запросы уже ждут: рекомендация — начать с порога в районе трёх-пяти и повышать, пока задержка не выйдет на целевую. Размер пакета показывает, сколько система обрабатывает прямо сейчас, и лучше подходит для низкой задержки; порог подбирается наблюдением: взять максимум под нагрузкой, поставить чуть ниже и снижать до целевой задержки. Здесь же становится понятно, почему больший пакет одновременно повышает пропускную способность и задержку: обработка входа одних запросов прерывает генерацию других.
Платформы сводят эти сигналы к целям SLO. Планировщик NVIDIA Dynamo в версии 1.6 меняет число реплик обработки входа и генерации, чтобы удержать цели по времени до первого токена и межтокенной задержке; в режиме по умолчанию он опирается на пороги глубины очереди и занятости KV-кеша. В руководстве llm-d по автомасштабированию один из вариантов пересчитывает накопленный необработанный вход в секунды ожидания и сравнивает их с долей бюджета на первый токен — тот же расчёт, что и в учебном примере выше. Там же отмечено, что при активном формировании пакетов загрузка ускорителя почти всегда близка к 100 %, поэтому как сигнал она бесполезна. Сигнал везде один и тот же: очередь и память.
Правило сработало — и ничего не произошло ещё несколько минут
Дальше начинается то, что отличает эксплуатацию инференса от эксплуатации веб-сервиса. Масштабирование означает новый под, а новый под платит полный холодный старт: скачать образ, поднять среду, загрузить гигабайты весов, скомпилировать графы вычислений. До первой полезной работы проходят минуты — а очередь, ради которой правило сработало, уже сформировала хвост.
Исследования атакуют этот интервал с двух сторон. ServerlessLLM (OSDI 2024) держит веса на локальных дисках и в памяти узлов, загружает их в 3,6–8,2 раза быстрее стандартных форматов и сокращает задержку на 10–200× в зависимости от нагрузки; подробности — в разборе холодного старта. BlitzScale (OSDI 2025) загружает веса по вычислительной сети между ускорителями и переносит вычисление отдельных слоёв на новые экземпляры, не дожидаясь полной загрузки: авторы сообщают о хвосте задержки до 94 % ниже, чем у ServerlessLLM, и о 49 % экономии времени ускорителей.
В открытых инструментах тот же интервал сокращают пробуждением вместо загрузки. Руководство llm-d по быстрому подъёму модели описывает подход, при котором тензоры остаются в оперативной памяти и возвращаются на ускоритель за секунды, а запуск нового экземпляра с уже загруженными модулями занимает десятки секунд. Проект честно оговаривает, что ускоряет подъём мощности, а не сам инференс, и оценивает накладные расходы резидентных серверов примерно в 2,5 процента памяти хоста.
Числа vLLM для этого режима: на A100 пробуждение занимает 0,26 секунды для Qwen3-0.6B и 0,82 секунды для Phi-3-vision против холодного старта в 37–58 секунд. На A100 переключение модели ускоряется в 18–20 раз, а верхняя граница заявленного диапазона — около 200 раз — получена на A4000. Это замеры проекта на своих моделях и своём железе, и относиться к ним стоит как к заявлению поставщика, а не как к ожидаемому результату на вашем контуре.
Практический вывод из этого раздела не про инструменты. Пока реакция на рост нагрузки измеряется минутами, запас ёмкости — не расточительство, а плата за физику. Тот же сюжет уже разбирался на уровне платформы в целом: сложность не исчезает, она переезжает — из приложения в платформу, а из платформы в того, кто её эксплуатирует. Отдельный разбор про Kubernetes описывает эту же механику для .
Цена запаса против цены нарушения
Всё предыдущее сводится к одному управленческому выбору. Запас ёмкости стоит денег каждый час, независимо от того, пригодился он или нет. Нарушение обещания стоит денег в момент нарушения — и стоимость эта распределена неравномерно: для внутреннего инструмента она близка к раздражению, для сценария в продукте измеряется брошенными сессиями.
За один период: расходы на запас = цена часа запаса × часы; потери от нарушений = число запросов × доля нарушенных запросов × средняя цена одного нарушения
Сравнивать нужно расходы за один и тот же период и сценарий нагрузки. Потери считают для каждого сценария с его долей нарушений; дополнительные расходы на запас оправданы, если ожидаемое уменьшение потерь стоит больше.
Соблазн здесь — посчитать только левую часть, потому что она приходит счётом от поставщика. Правая часть требует, чтобы кто-то назвал цену нарушенного обещания, а это разговор не инженерный. Но без него обсуждение ёмкости всегда заканчивается одинаково: «дорого» побеждает «медленно», пока «медленно» не станет заметно снаружи.
Учебный расчёт: точка безубыточности запаса
Назвать цену нарушения трудно, поэтому полезно перевернуть вопрос: посчитать, при какой цене запас окупается. Все числа ниже условные, цены — в условных единицах.
- интерактивный сценарий: 1,2 млн запросов в сутки, всплески в рабочие часы;
- без запаса автомасштабирование догоняет каждый всплеск через несколько минут холодного старта, и за сутки 25 000 запросов нарушают порог на первый токен;
- одна дополнительная реплика, работающая круглосуточно, стоит 40 у. е. в час, то есть 960 у. е. в сутки, и снимает 90 % нарушений — 22 500 запросов.
цена нарушения, при которой запас окупается = 960 ÷ 22 500 ≈ 0,043 у. е.
Теперь разговор с продуктом сводится к одному вопросу: стоит ли одно нарушенное обещание больше четырёх сотых условной единицы. Для платного помощника в коде ответ почти наверняка «да», для внутреннего инструмента — возможно, «нет». У решения есть и промежуточные формы. Если всплески приходятся на восемь рабочих часов, запас по расписанию стоит 320 у. е. в сутки, и порог окупаемости падает до 0,014 у. е. на нарушение. Если пробуждение спящей реплики сокращает холодный старт до секунд, нужный запас уменьшается ещё сильнее: окно, которое он закрывает, становится короче.
Полезно считать не токены, а принятую работу — ту же единицу, что и в разборе экономики AI-разработки. Тогда запас перестаёт быть строкой расходов и становится множителем: он повышает долю запросов, которые дошли до принятого результата.
Честная граница раздела. Расчёт показывает форму решения, а не его итог. Подставить в него чужие числа нельзя: цена часа зависит от контракта, доля нарушений — от профиля трафика и скорости подъёма реплик, а цена нарушения — от сценария. Точку безубыточности стоит пересчитывать на своих замерах каждый раз, когда меняется хотя бы одно из трёх.
Чеклист эксплуатации
Собранное в одном месте. Если из статьи нужно унести одну страницу — эту.
- Записать обещание как пару порогов и долю запросов внутри них; отдельно для интерактивных сценариев и для пакетных. Для агентских сценариев пересчитать обещание на уровень задачи, а не одного вызова.
- Развести три величины на дашборде: время до первого токена, темп генерации, сквозную задержку. Не усреднять их в одну кривую.
- Вывести время в очереди отдельной метрикой: это граница между ответственностью модели и ответственностью ёмкости.
- Разложить порог на первый токен в бюджет и получить из остатка на очередь порог тревоги по числу ожидающих запросов и лимит необработанного входа.
- Посчитать ёмкость в токенах KV-кеша по архитектуре модели и сверить со строкой о размере кеша при старте движка; планировать по распределению длины контекста, а не по числу пользователей.
- Смотреть на занятость KV-кеша и число вытеснений как на ранний признак: они меняются раньше, чем задержка.
- Ограничить приём на входе: короткая очередь и ранний отказ лучше длинной очереди и позднего таймаута. В vLLM лимиты очереди по умолчанию выключены.
- Разметить запросы приоритетами и проверить направление шкалы в своём движке и маршрутизаторе.
- Определить деградацию заранее — длина, модель, режим, глубина поиска — и регулярно исполнять этот путь.
- Ввести бюджет ретраев и экспоненциальную задержку: в инференсе повтор заново занимает память под весь контекст.
- Масштабировать по очереди или по размеру пакета, а не по загрузке ускорителя.
- Заложить в план холодный старт реплики: правило срабатывает мгновенно, мощность появляется через минуты.
- Посчитать точку безубыточности запаса и пересчитывать её, когда меняется профиль трафика или скорость подъёма реплик.
Что стоит унести с собой
- 01Обещание продукту — это пара порогов и доля запросов внутри них, а не число токенов в секунду. Индустрия записала такой формат раньше вас: MLPerf оценивает систему по устойчивой нагрузке внутри границ TTFT и TPOT, а исследователи называют эту меру полезной пропускной способностью.
- 02Три величины нельзя складывать в одну кривую: время до первого токена, темп генерации и сквозная задержка ломаются по разным причинам и лечатся разными ручками. Порог на первый токен раскладывается в бюджет, а остаток на очередь через закон Литтла превращается в порог тревоги.
- 03Ёмкость инференса измеряется памятью под контексты, а не числом запросов. В учебном узле из четырёх H100 с Llama 3.1 70B одна сессия на 128 тысяч токенов занимает место 43 обычных диалогов, и вытеснение видно отдельной метрикой раньше, чем жалобой.
- 04Ретрай в инференсе дороже, чем в обычном сервисе: он заново занимает память под весь контекст. Поэтому бюджет ретраев и заранее отрепетированная деградация здесь не гигиена, а несущая конструкция.
- 05Запас ёмкости — не расточительство, а плата за то, что реакция на рост измеряется минутами: новая реплика платит полный холодный старт до первой полезной работы. Вместо спора «дорого или медленно» полезно посчитать цену нарушения, при которой запас окупается.
Документация, стандарты и исследования
Список сгруппирован по роли источника. Пороги бенчмарков, настройки и метрики движков проверены по первичным страницам на 31 октября 2026 года; заявления проектов о собственных решениях и статус препринтов помечены в примечаниях.
Пороги и бенчмарки
- MLCommons · Llama 3.1-8B в MLPerf Inference v5.1 — источник порогов: Server — TTFT ≤ 2 с и TPOT ≤ 100 мс, Interactive — 0,5 с и 30 мс; там же перевод в слова в минуту
- MLCommons · MLPerf Inference v6.1 — раунд 16 сентября 2026 года: 30 участников, новые тесты агентного инференса и RAG, объявленная замена MLPerf Inference на MLPerf Endpoints для датацентров
- MLCommons · loadgen/mlperf.conf — пороги TTFT и TPOT по моделям и сценариям: Llama 2 70B Interactive — 450 и 40 мс, DeepSeek-R1 Interactive — 1500 и 15 мс
Движки инференса
- vLLM · Prometheus-метрики — перечень метрик, на которые опирается статья: TTFT, межтокенная задержка, время в очереди, занятость KV-кеша, вытеснения
- vLLM · Optimization and Tuning — вытеснение при нехватке KV-кеша, режим RECOMPUTE по умолчанию в V1, ручки против вытеснения и компромисс при смешивании обработки входа с генерацией
- vLLM · SchedulerConfig — политики fcfs и priority, лимиты max_num_queued_reqs и max_num_queued_tokens с ответом 503; второй лимит предложено задавать как целевое TTFT × скорость обработки входа
- vLLM · Engine Arguments — доля памяти движка по умолчанию 0,92, варианты типа KV-кеша, включая FP8
- vLLM · Parallelism and Scaling — что означают строки о размере KV-кеша и предельной параллельности при старте и когда добавлять ускорители
- vLLM · релиз v0.32.0 — актуальная версия на 31 октября 2026 года (18 октября)
- SGLang · Server Arguments — политики планировщика, --schedule-conservativeness, --chunked-prefill-size, --max-running-requests и приоритеты, где по умолчанию раньше идут большие значения
- SGLang · Hyperparameter Tuning — советы для пропускной способности офлайн-обработки: занятость KV-кеша выше 0,9 и очередь в сотни запросов; статья показывает, почему для интерактивного обещания это не цель
- Meta · llama-models, параметры архитектуры — Llama 3.1 70B: 80 слоёв, 64 головы внимания, 8 KV-голов, размерность 8192 — основа учебного расчёта KV-кеша
Исследования: планирование и SLO
- Zhong et al. · DistServe (OSDI 2024) — определение полезной пропускной способности через долю запросов внутри SLO; 1,6 против 3,3 запроса в секунду на ускоритель во вводном примере; до 7,4× запросов или в 12,6 раза более жёсткий SLO
- Agrawal et al. · Sarathi-Serve (OSDI 2024) — остановки генерации длиной до нескольких секунд, нарезка обработки входа и до 2,6× ёмкости для Mistral-7B при ограничении на хвост межтокенной задержки
- Sun et al. · Llumnix (OSDI 2024) — живая миграция запросов вместе с KV-кешем: хвост задержки лучше на порядок, приоритетные запросы до 1,5× быстрее, до 36 % экономии
- Patke et al. · QLM (SoCC 2024) — управление очередью по оценке времени ожидания: доля запросов внутри SLO +40–90 %, пропускная способность +20–400 % на требованиях облачного провайдера
- Chen et al. · SLOs-Serve — препринт апреля 2025 года о распределении токенов под ограничения SLO; ×2,2 к ёмкости на ускоритель на шести сценариях; на 31 октября 2026 года площадка не подтверждена
- Liu et al. · Andes — препринт о качестве потоковой выдачи: скорость чтения 4,8 и прослушивания 3,3 токена в секунду, выдача быстрее чтения не улучшает опыт
Исследования: память, нагрузка и холодный старт
- Kwon et al. · PagedAttention и vLLM (SOSP 2023) — в прежних системах на реальные состояния токенов уходило 20,4–38,2 % памяти KV-кеша; страничная раскладка дала 2–4× пропускной способности
- Patel et al. · Splitwise (ISCA 2024) — трейсы Azure: у помощника в коде медианный вход 1500 и ответ 13 токенов, у диалогового сервиса — 1020 и 129
- Wang et al. · BurstGPT (KDD 2025) — 213 дней трейсов региональных сервисов Azure OpenAI: всплески меняются в рабочие часы, доля отказов диалогового сервиса ChatGPT выше 5 %
- Qin et al. · Mooncake (FAST 2025) — архитектура сервиса Kimi: упреждающий отказ запросам, которые не уложатся в SLO; на 115 % и 107 % больше запросов на A800 и H800
- Qin et al. · Mooncake, расширенная версия — перегрузка в часы пик и эксперимент с ранним отказом на записи из 23 тысяч запросов: 4183, 3771 и 3589 отклонённых запросов
- Fu et al. · ServerlessLLM (OSDI 2024) — загрузка весов в 3,6–8,2 раза быстрее и снижение задержки на 10–200× при холодном старте
- Zhang et al. · BlitzScale (OSDI 2025) — загрузка весов по сети ускорителей и послойное масштабирование: хвост задержки до 94 % ниже ServerlessLLM, 49 % экономии времени ускорителей
Эксплуатация и перегрузка
- Google SRE · Addressing Cascading Failures — очередь как расход памяти и задержки, пример с запросом в 1,1 секунды, сброс нагрузки, деградация и серверный бюджет повторов
- Google SRE · Handling Overload — адаптивное ограничение на клиенте с K = 2, три попытки на запрос и бюджет повторов в 10 %: рост нагрузки около 1,1× вместо почти 3×
- Dean, Barroso · The Tail at Scale (CACM 2013) — 1 % медленных ответов у каждого из ста серверов делает медленными 63 % запросов; основа расчёта для цепочек вызовов агента
- Little · A Proof for the Queuing Formula: L = λW (1961) — закон Литтла, по которому бюджет ожидания переводится в число запросов в очереди
- Kingman · The single server queue in heavy traffic (1961) — приближение среднего ожидания в очереди G/G/1 произведением загрузки, вариабельности и времени обслуживания
Платформа и маршрутизация
- Google Cloud · автомасштабирование инференса на GKE — почему загрузка GPU — плохая метрика, как подбирать порог по очереди и по размеру пакета; страница обновлена 30 октября 2026 года
- Kubernetes · Introducing Gateway API Inference Extension — почему обычная балансировка не подходит длинным сессиям с частичным состоянием; что описывает InferencePool
- Gateway API Inference Extension · релиз v1.6.0 — август 2026 года: компонент выбора эндпоинта и InferenceObjective переехали в llm-d, проект сосредоточен на стандарте и тестах совместимости
- llm-d · релиз v1.0.0 — 20 октября 2026 года: управление потоком с приоритетами и сбросом нагрузки, DisaggregatedSet для пулов двух стадий
- llm-d · Flow Control — очереди по приоритетам, отрицательный приоритет как признак сбрасываемого запроса, ответ 429 при исчерпании лимита; механизм включается отдельной настройкой
- llm-d · Workload Autoscaling — масштабирование по очереди, запросам в работе и накопленным токенам; пересчёт необработанного входа в секунды ожидания против бюджета TTFT
- llm-d · Fast Model Actuation — пробуждение спящего экземпляра за секунды и запуск через подготовленный загрузчик за десятки секунд; заявление проекта о собственном решении
- vLLM · Zero-Reload Model Switching with Sleep Mode — числа пробуждения против холодного старта на A100 и два уровня сна; 18–20× на A100, около 200× на A4000; заявление проекта
- NVIDIA Dynamo · Planner Guide — версия 1.6: число реплик двух стадий подстраивается под цели TTFT и межтокенной задержки; по умолчанию — пороги очереди и занятости KV-кеша
Связанные материалы
- Инференс LLM как распределённая система →как устроены пакеты, KV-кеш и разделение стадий
- Фронтир инференса LLM →где граница доказанного выигрыша
- Локальный инференс: модели, железо, скорость и цена →та же арифметика памяти на одной машине
- Research Insights №32 · vLLM и PagedAttention →почему ёмкость измеряется памятью
- Разбор Llumnix · миграция запросов →перенос запроса вместо вытеснения
- Разбор ServerlessLLM · холодный старт →откуда берутся минуты до первой работы
- Карта кластера инференса →итог серии разборов об инференсе на кластере
- Kubernetes: кто его усложнил? Куда переехала сложность платформы →
- Экономика AI-разработки: от токенов к принятой работе →