Содержание
  1. Основной механизм и его пределы
  2. Учебный вход, артефакт и эталон проверки
  3. Что проверять на реальной нагрузке
  4. Первоисточники: что именно утверждает каждый
  5. Случай, где подход не решает задачу
  6. Как это устроено в разных движках
  7. Частые вопросы
Справочник

Что такое continuous batching в LLM-сервере

11 ноября 2026 · 15 минут чтения

Что такое continuous batching в LLM-сервере: это планирование пакета по шагам генерации: на каждом шаге сервер проверяет завершившиеся последовательности и ставит на освободившееся место новые запросы. Документация Transformers описывает это так: пакет динамически пересобирается на каждом шаге, освободившиеся места занимают новые запросы, графический ускоритель остаётся загружен, суммарная пропускная способность держится высокой.

Основной механизм и его пределы

Разберём, из чего складывается генерация. По статье Hugging Face «Continuous batching from first principles», модель обрабатывает текст токенами: слово иногда состоит из нескольких токенов, и для каждой последовательности сеть вычисляет предсказание следующего токена. Часть операций в сети работает по каждому токену отдельно — нормализация слоя, умножение матриц. Связи между словами создаёт внимание, и это единственное место, где токены влияют друг на друга.

Дальше начинается арифметика, которая объясняет серверные решения. В той же статье показано: тензоры запросов и ключей перемножаются, получается матрица размера n на n, поэтому внимание имеет квадратичную сложность по длине последовательности, а вычисление требует порядка n²d операций. Длина растёт — стоимость растёт квадратом длины. Поверх результата накладывается причинная маска: токен взаимодействует с токенами, которые шли до него.

Практический вывод из этого устройства такой. Anyscale в разборе про continuous batching пишет, что для рассмотренного ими режима декодирования важным ограничением стала пропускная способность памяти: загрузка мегабайта данных в вычислительные ядра занимает больше времени, чем сами вычисления над этим мегабайтом. Размер пакета позволяет распределить чтение весов между несколькими запросами; его предел зависит от доступной памяти. Характер ограничения меняется с фазой, размером пакета и моделью.

Отсюда два разных показателя, которые нельзя смешивать. Первый — суммарная пропускная способность сервера: сколько токенов в секунду он отдаёт на всех запросах вместе. Второй — задержка отдельного запроса: сколько ждёт конкретный пользователь. Continuous batching работает с первым показателем. Он поднимает заполнение ускорителя за счёт того, что пустые места в пакете заполняются сразу, как только освобождаются.

Предел механизма лежит там же, где и его сила. Планировщик переставляет запросы между шагами, но каждый шаг всё равно требует прохода по всем активным последовательностям. Если памяти хватает на малое число последовательностей, планировщику нечего переставлять: свободных мест в пакете нет, и новые запросы ждут в очереди. Anyscale называет это ограничением по памяти и указывает, что подход не требует менять веса модели — он перераспределяет уже имеющуюся память.

Второй предел — фаза предварительной обработки промпта. Prefill обрабатывает входные токены и создаёт состояния внимания для продолжения. При длинном промпте он способен задержать выдачу токенов другим запросам. Влияние зависит от планировщика: движки могут разделять prefill на части или по-разному сочетать его с decode. Само название continuous batching ещё не определяет этот порядок.

Третий предел — разнородность запросов. Длительность генерации заранее неизвестна: Anyscale напоминает, что модель выдаёт токены до стоп-токена или до достижения максимальной длины последовательности. Планировщик не знает, сколько шагов займёт каждый запрос, и принимает решения по факту завершения.

Названия стоит развести, потому что в источниках они пересекаются. Anyscale пишет, что continuous batching называют также dynamic batching и планированием на уровне итераций. В документации Transformers dynamic scheduling — часть внутреннего устройства менеджера. В этой статье под continuous batching понимается планирование на уровне шага генерации: пакет пересобирается между шагами. Планирование на уровне запроса, request-level batching, собирает пакет один раз при поступлении группы запросов и держит его до конца самой долгой генерации.

Учебный вход, артефакт и эталон проверки

Ниже — полное условное расписание для разбора механизма. Его значения заданы для иллюстрации; измерений сервера не проводилось. Приняты одинаковое время шага и отсутствие фазы prefill; оба допущения служат только иллюстрации.

Условия такие. Два слота в пакете. Все четыре запроса стоят в очереди с самого начала. Длительности декодирования: А — 2 шага, Б — 5 шагов, В — 1 шаг, Г — 2 шага. Всего полезной работы 10 слотошагов.

При статическом пакетировании расписание выглядит так:

ТЕКСТ8 строк
Static batching, 2 слота, шаги 1-7
шаг 1: А | Б
шаг 2: А | Б
шаг 3: пусто | Б
шаг 4: пусто | Б
шаг 5: пусто | Б
шаг 6: В | Г
шаг 7: пусто | Г

При continuous batching расписание меняется:

ТЕКСТ6 строк
Continuous batching, 2 слота, шаги 1-5
шаг 1: А | Б
шаг 2: А | Б
шаг 3: В | Б
шаг 4: Г | Б
шаг 5: Г | Б

Разберём, почему В появляется на шаге 3. Запросы А и Б заняли оба слота на шаге 1. А завершается после шага 2 — его два шага декодирования закончились. На шаге 3 планировщик видит освободившийся слот и ставит туда В, который ждал в очереди с самого начала. В укладывается в один шаг и уходит. На шаге 4 освободившийся слот занимает Г. К шагу 5 в пакете остаются Г и Б, и оба завершаются.

Сравним два расписания по занятости. В статическом варианте доступно 14 слотошагов за 7 шагов, полезной работы 10, условная занятость 10/14. В continuous-варианте доступно 10 слотошагов за 5 шагов, полезной работы те же 10, условная занятость 10/10. Разница возникает из пустых клеток: в статическом расписании их четыре, в continuous-варианте их нет.

Эталон проверяют отдельно для каждого режима. В обоих расписаниях каждый запрос получает заданное число шагов, число последовательностей не превышает два слота, а очередь обслуживается в исходном порядке. В static-варианте следующий пакет начинает работу после завершения всего предыдущего пакета: В и Г появляются на шаге 6. В continuous-варианте ожидающий запрос занимает первый освободившийся слот: В появляется на шаге 3, Г — на шаге 4. Правило немедленного заполнения свободного слота относится к continuous-варианту; пустой слот в static-расписании соответствует заданной модели.

Здесь важно развести структурную корректность и фактическую правильность. Структурная корректность — это соответствие расписания правилам планирования: слоты не переполнены, длительности соблюдены, очередь не переставляется. Фактическая правильность — это соответствие расписания поведению конкретного сервера на конкретном оборудовании. Учебное расписание может быть структурно безупречным и при этом не описывать реальную систему: в настоящем сервере шаги различаются по времени, обработка prefill зависит от планировщика, а длительности генерации заранее неизвестны. Совпадение с правилом подтверждает понимание учебной схемы. Поведение продукта проверяют отдельным запуском.

Что проверять на реальной нагрузке

Учебное расписание отвечает на вопрос «как устроено планирование». На вопрос «помогло ли это на моём сервере» отвечает отдельная проверка. Ниже — карточка такой проверки. Это план действий; редакция модель не запускала.

Условия проверки. Одна модель и одно устройство. Одинаковые входы. Одинаковые моменты прихода запросов. Одинаковый набор параметров сэмплирования. Меняется только режим пакетирования: статический пакет против continuous batching.

Что фиксировать по каждому запросу:

Показатель Что означает Как читать
TTFT время до первого токена растёт, если длинный prefill задерживает старт
Межтокенная задержка пауза между соседними токенами показывает ровность выдачи у одного пользователя
Успешные запросы за интервал сколько запросов дошло до конца основной счётчик пропускной способности
Ошибки отказы и обрывы отдельный счётчик, не смешивать с задержкой
Память занятая память ускорителя объясняет, сколько слотов доступно

Порядок действий. Сначала прогоните статический пакет на фиксированном наборе входов и запишите все пять показателей. Затем прогоните тот же набор в режиме continuous batching и запишите те же показатели. Сравните распределение, включая медиану и хвост. Отдельно посмотрите на запросы, которые пришли позже остальных: именно они выигрывают от освобождения слотов, и именно у них видно разницу между режимами.

Что может пойти не так. Длинный prefill может ухудшить интерактивную задержку, даже если сервер выдаёт больше токенов суммарно. Такая задержка возможна, когда планировщик не оставляет другим запросам достаточно времени на decode. Если ваш сценарий чувствителен к TTFT, рост суммарной пропускной способности сам по себе задачу не решает.

Второй источник расхождений — параметры сэмплирования. Документация Transformers описывает флаг per_request_processors: он позволяет применять temperature, top_k и top_p независимо для каждого запроса в рамках одного прямого прохода. Это даёт разным запросам разные настройки в одном пакете. При проверке держите параметры одинаковыми, иначе сравнение режимов смешается с разницей в сэмплировании.

Третий источник — сортировка входов. В той же документации сказано, что add_requests() отправляет группу сразу и автоматически сортирует входы, чтобы повысить попадания в кэш префиксов при включённом разделении блоков. Сортировка меняет порядок обработки, и в показателях это отражается. Для сравнения зафиксируйте один способ подачи и одинаковую последовательность поступления. Подача по одному запросу полезна, если нужно сохранить заранее заданный порядок.

Первоисточники: что именно утверждает каждый

Внешние утверждения ниже принадлежат названным владельцам. Числа взяты из их публикаций и относятся к их условиям измерения.

Документация Transformers описывает два способа работы. generate_batch() принимает список токенизированных промптов и возвращает результаты, когда все они готовы; планирование выполняется внутри, вызов блокируется до завершения всех запросов. ContinuousBatchingManager запускает фоновый поток и позволяет отправлять запросы и забирать результаты независимо: на каждом шаге генерации он проверяет завершившиеся запросы и ставит в пакет новые. Этот режим подходит для потоковой выдачи, обслуживания в реальном времени и подачи запросов по мере поступления.

Там же описан порядок остановки. stop() останавливает фоновый поток: по умолчанию менеджер перестаёт принимать новые заявки и ждёт завершения очереди и активных запросов. Флаг hard_stop=True отменяет незавершённую работу немедленно, а ожидающие и активные запросы завершаются ошибкой RuntimeError. После вызова stop() методы add_request() и add_requests() отбрасывают новые заявки и пишут предупреждение в журнал; запустить сессию заново можно вызовом start. Метод destroy() освобождает распределённые ресурсы, останавливает менеджер, если тот ещё работает, и после него менеджер перезапустить нельзя. Контекстный менеджер continuous_batching_context_manager() вызывает stop при выходе и destroy, если не передан флаг persistent_manager=True, кэширующий менеджер на модели для следующей сессии.

Блог vLLM описывает отдельную линию оптимизации. Библиотека использует PagedAttention — алгоритм внимания, который управляет ключами и значениями. Кэш ключей и значений динамичен: его размер зависит от длины последовательности, которая сильно варьируется и плохо предсказуема. По оценке авторов, существовавшие системы теряли 60–80% памяти из-за фрагментации и избыточного резервирования. PagedAttention делит кэш каждой последовательности на блоки фиксированного размера и хранит непрерывные ключи и значения в несмежном пространстве памяти; логические блоки последовательности отображаются на физические через таблицу блоков, а физические блоки выделяются по мере генерации новых токенов. В такой схеме потери памяти остаются только в последнем блоке последовательности — на практике меньше 4%. Авторы сообщают о сравнении с HuggingFace Transformers и Text Generation Inference на LLaMA-7B на NVIDIA A10G и LLaMA-13B на NVIDIA A100 (40GB) с длинами из набора ShareGPT: до 24x выше пропускной способности относительно Transformers и до 3.5x относительно TGI. Разделение памяти через таблицу блоков снижает накладные расходы сложных схем сэмплирования, включая параллельное сэмплирование и поиск по лучу, до 55%, что даёт до 2.2x прироста пропускной способности.

Anyscale приводит свои цифры: 23x пропускной способности вывода при использовании vLLM со снижением p50-задержки, а также 8x относительно наивного пакетирования при использовании continuous batching на Ray Serve и в text-generation-inference от Hugging Face. Эти значения относятся к условиям их эксперимента и к названным системам.

Документация Ray Serve LLM описывает совместимость: сервис предоставляет API, совместимый с OpenAI, и повторяет сервер vLLM с тем же интерфейсом; большинство параметров engine_kwargs, работающих с vllm serve, работают и здесь. Через эту связку доступны эмбеддинги, структурированный вывод, мультимодальные модели и модели рассуждения. Эмбеддинги в vLLM относятся к категории Pooling Models и включаются параметром pooler_config со значением {«task»: «embed»}. Структурированный вывод запрашивается в режиме JSON или через валидацию схемы JSON моделями Pydantic. Модели рассуждения, включая DeepSeek-R1 и QwQ, используют расширенный процесс размышления перед финальным ответом.

Числа из этих публикаций принадлежат их авторам и их условиям. Переносить значения между продуктами нельзя: 24x относится к сравнению vLLM с Transformers на названных конфигурациях, 23x — к эксперименту Anyscale, 8x — к их же сравнению с наивным пакетированием. Версии, порты и ограничения одного продукта не описывают другой.

Случай, где подход не решает задачу

Continuous batching повышает заполнение ускорителя. Есть сценарии, где это не даёт нужного результата.

Сценарий первый: один длинный запрос. Пользователь отправляет объёмный документ и ждёт развёрнутый ответ. В пакете одна последовательность, свободных слотов сколько угодно, ставить в них некого. Планировщик пересобирает пакет между шагами, но пересобирать нечего. Перезаполнять освободившиеся слоты здесь некому: задержка по-прежнему зависит от prefill и decode, а выигрыш от совместного обслуживания отсутствует.

Сценарий второй: жёсткое требование к TTFT при смешанной нагрузке. Интерактивное приложение обещает первый токен за фиксированное время. В очереди оказывается запрос с очень длинным промптом. Если планировщик задерживает decode на время длинного prefill, первый токен интерактивного запроса может прийти позже. Даже возможный рост суммарной пропускной способности этого не компенсирует. Требование по задержке нарушено, и рост счётчика токенов его не восстанавливает.

Сценарий третий: память под последовательности исчерпана. Планировщик работает по шагам, но число одновременных последовательностей ограничено доступной памятью под кэш ключей и значений. Когда все слоты заняты длинными генерациями, новые запросы ждут освобождения. Задача сводится к управлению памятью, и здесь работают другие приёмы: разбиение кэша на блоки, разделение блоков между последовательностями, снижение разрядности представления весов. Anyscale прямо относит continuous batching к оптимизациям памяти, которые не требуют изменения весов модели, и указывает, что квантование и переработка вычисления внимания решают смежную задачу.

Сценарий четвёртый: короткие запросы при низкой нагрузке. Если сервер обслуживает единичные обращения, пакет всё равно состоит из одной-двух последовательностей. Пересборка пакета между шагами добавляет работу планировщика, а заполнять нечего. Выигрыш появляется при одновременной нагрузке, когда в очереди есть кому занять освободившийся слот.

Общего обещания ускорения здесь нет. Есть условие применимости: несколько одновременных запросов разной длительности, достаточная память под пакет и проверка допустимой задержки отдельных запросов вместе с суммарной пропускной способностью.

Как это устроено в разных движках

Механизм планирования по шагам встречается в нескольких серверных проектах, и каждый владелец описывает свою реализацию.

Проект Что описано в документации Что проверять читателю
Transformers generate_batch() с внутренним планированием; ContinuousBatchingManager с фоновым потоком; per_request_processors для независимых параметров сэмплирования порядок остановки менеджера и поведение при hard_stop
vLLM PagedAttention для управления кэшем ключей и значений; блоки фиксированного размера; таблица блоков; разделение блоков с подсчётом ссылок и копированием при записи распределение памяти под блоки и поведение при параллельном сэмплировании
Ray Serve LLM API, совместимый с OpenAI, и совместимость параметров с vllm serve; эмбеддинги, структурированный вывод, мультимодальные и reasoning-модели какие параметры движка переносятся, а какие задаются на уровне развёртывания
text-generation-inference упоминается в измерениях Anyscale как система, на которой проверялся continuous batching условия их эксперимента и состав нагрузки

Читателю, который выбирает движок, полезно держать в голове три вопроса. Первый: как движок управляет памятью под кэш ключей и значений — от этого зависит число одновременных последовательностей. Второй: как он обходится с фазой предварительной обработки промпта — смешивается ли она с декодированием в одном шаге или идёт отдельно. Третий: какие параметры сэмплирования можно задавать на отдельный запрос. Ответы на эти вопросы лежат в документации конкретного проекта, и переносить их между проектами нельзя.

Отдельно про имена. Anyscale называет continuous batching также dynamic batching и планированием на уровне итераций. В документации Transformers dynamic scheduling — часть работы менеджера. Когда встречаете термин в чужом тексте, смотрите, что именно описано: пересборка пакета между шагами генерации или сборка пакета на уровне группы запросов. От этого зависит, применима ли к вашему случаю описанная оптимизация.

Частые вопросы

Что такое continuous batching простыми словами?

Это способ обслуживать несколько запросов к языковой модели одновременно, пересобирая пакет на каждом шаге генерации. Как только одна последовательность завершается, её место в пакете занимает следующий запрос из очереди. Документация Transformers описывает результат так: ускоритель остаётся загруженным, суммарная пропускная способность держится высокой. Статический пакет ждёт завершения самой долгой генерации, и освободившиеся места простаивают.

Чем continuous batching отличается от статического пакетирования?

Статический пакет собирается один раз и держится до конца самой долгой генерации; короткие запросы завершаются, а их места остаются пустыми. Continuous batching пересобирает пакет между шагами и заполняет освободившиеся места сразу. В учебном расписании с двумя слотами статический вариант даёт занятость 10/14 за 7 шагов, continuous-вариант — 10/10 за 5 шагов при той же полезной работе в 10 слотошагов.

Почему continuous batching повышает пропускную способность?

Подход заполняет места, освободившиеся после коротких генераций. В рассмотренном Anyscale режиме decode существенным ограничением было чтение данных из памяти: совместная обработка позволяет лучше использовать эту работу. Выигрыш зависит от нагрузки и доступной памяти; число токенов за интервал нужно измерить.

Улучшает ли continuous batching задержку отдельного запроса?

Суммарная пропускная способность и задержка отдельного запроса — разные показатели. Длинный prefill может задержать интерактивный запрос; результат зависит от его сочетания с decode в конкретном планировщике. Проверяйте TTFT и распределение задержек вместе с суммарным числом токенов за интервал.

Как проверить эффект на своём сервере?

Возьмите одну модель и одно устройство, одинаковые входы и одинаковые моменты прихода запросов. Прогоните набор в статическом режиме и в режиме continuous batching, фиксируя TTFT, межтокенную задержку, число успешных запросов за интервал, ошибки и занятую память. Сравнивайте медиану и хвост распределения, отдельно смотрите на запросы, пришедшие позже остальных. Держите параметры сэмплирования одинаковыми, иначе сравнение режимов смешается с разницей в настройках.

Читайте также

3 материала