中文翻译暂不可用,显示俄语原文。

Что такое continuous batching и как оно влияет на throughput?

Краткий тезис

batching|Continuous batching — это техника динамического управления батчами при инференсе LLM, при которой запросы добавляются и удаляются из батча после каждого шага декодирования, а не фиксируются на весь цикл генерации. В отличие от static batching, batching|непрерывный батчинг значительно повышает throughput (пропускную способность) за счёт более эффективного использования GPU, уменьшая простои, вызванные разной длиной генерации запросов. Типичный прирост throughput составляет 4–6x при одновременном снижении latency (задержки) на 30–50%.


1. Проблема статического батчинга

При static batching (статическом батчинге) сервер накапливает запросы, формирует фиксированный батч и передаёт его LLM. Батч обрабатывается последовательно шаг за шагом (итерациями декодирования), пока все запросы не сгенерируют свой ответ. Если один запрос генерирует 10 токенов, а другой — 500, GPU простаивает после завершения первого, ожидая, пока второй закончит.

  • Неравномерная длина ответов ведёт к "растянутым" батчам.
  • Для ускорения коротких запросов приходится ждать завершения всех.
  • Возникают пустые слоты (padding), если часть запросов уже завершена.

Это снижает GPU utilization (загрузку GPU). Типичная утилизация в batching|static batching — 30–50% для real-time трафика.


2. Определение continuous batching

Continuous batching (непрерывный батчинг) — подход, при котором планировщик на каждом шаге декодирования (итерации) пересматривает состав батча:

  • Запросы, сгенерировавшие End-of-Sequence (EOS), сразу удаляются.
  • Освободившиеся слоты занимаются новыми запросами из очереди ожидания.
  • Каждый запрос проходит свои стадии: prefill (обработка промпта) и decode (авторегрессивная генерация) независимо от других.

Таким образом, батч динамически обновляется каждую итерацию. Этот механизм называют iteration-level scheduling (планирование на уровне итераций).


3. Как работает iteration-level scheduler (на примере vLLM)

Самый известный open-source фреймворк, реализующий continuous batchingvLLM. Его планировщик работает так:

  1. Очередь ожидания Входящие запросы помещаются в очередь.
  2. Формирование батча На итерации планировщик выбирает из очереди столько запросов, сколько помещается в свободную память (ключи/значения внимания — KV cache).
  3. Prefill Новые запросы проходят фазу prefill (вычисление первого токена). Эта фаза выполняется эффективно как batch matrix multiplication.
  4. Decode: После prefill запросы переходят в фазу decode, где на каждой итерации генерируется один следующий токен.
  5. Завершение Как только запрос доходит до EOS или до max_tokens, он удаляется из батча; его память освобождается.
  6. Заполнение пустот На следующей итерации освободившиеся KV-слоты могут быть заняты новыми prefilling-запросами или дополнительными decoding-запросами.

Ключевой элемент — PagedAttention (см. вопрос о памяти), который позволяет управлять KV-кэшем блоками, а не непрерывными буферами, что облегчает динамическое добавление/удаление.


4. Влияние на throughput

Throughput (пропускная способность) — количество запросов (или токенов), обработанное системой в единицу времени.

ФакторStatic batchingContinuous batchingПрирост
GPU Utilization30–50% (из-за простоя в конце)70–95% (почти всегда занят)+50–100%
Время обработки батчаРавно макс. длине генерацииСумма по всем итерациям, слоты не простаиваютСнижение в 2–6 раз
Эффективный throughputОграничен worst-case длинойПриближается к average длине+4–6x

Причина В static batching GPU простаивает, когда часть запросов уже закончила генерацию. В continuous batching на освободившиеся слоты сразу поступают новые запросы, поддерживая высокую загрузку.

Математическая иллюстрация Пусть t<sub>i</sub> — длина генерации i-го запроса. Для static batching одного батча из N запросов время ~ max(t<sub>i</sub>) × N<sub>tok</sub>? Нет, точнее: каждая итерация занимает одно и то же время (прямой проход). Время батча = max(t<sub>i</sub>) × (время итерации). Все запросы проходят одинаковое количество шагов, равное максимальной длине. Аппаратные ресурсы используются только для активных запросов на каждом шаге.

Continuous batching убирает холостые итерации: каждый запрос обрабатывается ровно t<sub>i</sub> шагов. GPU всегда параллельно обрабатывает столько запросов, сколько помещается в память. Эффект сильнее при большом разбросе длин ответов.


5. Влияние на latency

Latency (задержка) — время от отправки запроса до получения первого или последнего токена.

  • Static batching: время ожидания в очереди + время обработки батча (макс. длина). Для коротких запросов latency может быть высокой, если они попали в батч с длинными.
  • Continuous batching: запросы быстрее попадают в исполнение (слоты освобождаются чаще). Средняя latency снижается на 30–50%.
  • Однако для отдельных долгих запросов latency может немного возрасти из-за конкуренции с новыми запросами (но обычно это компенсируется более частым выходом токенов).

Важно Continuous batching улучшает P50/P95 latency для большинства пользователей, особенно когда нагрузка переменная.


6. Сравнение в таблице

ХарактеристикаStatic BatchingContinuous Batching
Состав батчаФиксирован на весь цикл генерацииМеняется на каждой итерации
Завершение запросаЖдёт окончания всехУдаляется сразу после EOS
Добавление новых запросовТолько после завершения батчаНа любой итерации (если есть место)
GPU Utilization30–50%70–95%
Throughput (отн. единицы)1× (базовый)4–6×
Latency (P50)Высокая для короткихНиже на 30–50%
Сложность реализацииНизкая (простой queue)Высокая (scheduler + управление памятью)
Потребление памяти (KV cache)Фиксированный буфер под батчДинамическое выделение (PagedAttention)

7. Дополнительные аспекты и trade-offs

  • Memory overhead Continuous batching требует более сложного управления KV-кэшем. Без PagedAttention было бы много фрагментации.
  • Fairness (справедливость): Планировщик может отдавать приоритет prefilling или decoding. Некоторые схемы (например, равномерные квоты) предотвращают голодание длинных запросов.
  • Preemption (вытеснение): Если память исчерпана, можно приостановить part of decoding-запросы, чтобы пропустить prefilling (или наоборот). Это ещё один уровень динамики.
  • Batch formation latency На каждую итерацию планировщик тратит время на перетасовку (CPU overhead). В хорошо оптимизированных системах (vLLM) это время negligible.
  • Ограничения Continuous batching плохо работает при очень маленьких батчах (1–2 запроса) — оверхед больше выгоды. Но для production с сотнями concurrent запросов — это стандарт.

8. Реализации в open-source

ФреймворкМеханизмОсобенности
vLLMIteration-level scheduler + PagedAttentionДе-факто стандарт, легко конфигурируется
TensorRT-LLMIn-flight batching (аналог continuous)Оптимизация под NVIDIA GPU, поддержка FP8
LightLLMDynamic batching на основе очередейЛегковесная альтернатива
TGI (HuggingFace)Continuous batching (через расширение)Простая интеграция с HF models

9. Когда continuous batching неэффективен

  • Offline batch inference (например, генерация summary для корпуса): можно заранее отсортировать запросы по длине, что даёт ту же выгоду без overhead continuous batching.
  • При очень малом размере батча (1–2 запроса) выигрыш незначителен.
  • Если все запросы имеют одинаковую длину (синтетический сценарий), разница с static batching минимальна.

Пет-проект для закрепления

Задача Реализовать симулятор continuous batching и сравнить его throughput с static batching.

Инструменты Python + NumPy (или PyTorch для более реалистичной модели времени).

Шаги:

  1. Модель симуляции (mock LLM): Каждый запрос имеет случайную длину генерации (например, от 10 до 500 токенов). Время одной итерации decode = constant (0.1 ms), Prefill = пропорционально числу входных токенов (0.2 ms / токен).
  2. Static batching:
    • Накопить N запросов (например, 128).
    • Запустить batch: выполнить prefills, затем decode min(max_len) шагов.
    • Замерить общее время. Перейти к следующему batch.
  3. Continuous batching:
    • Очередь запросов и динамический батч размером до max_batch_size (например, 64).
    • На каждом шаге: обработать prefills (если есть новые запросы) и decode всех активных.
    • Удалять завершённые.
    • Собирать статистику.
  4. Сравнение
    • Throughput = ∑ длины / total_time.
    • Latency (время от начала до конца каждого запроса) — гистограмма.
  5. Эксперименты
    • Изменить distribution длин (увеличить разброс).
    • Попробовать разные max_batch_size.
    • Добавить механизм priority (prefill vs decode).

Ожидаемый результат Симулятор покажет, что continuous batching даёт throughput в 2–5 раз выше, особенно при высоком разбросе длин, а P50 latency снижается. Код можно выложить на GitHub с визуализацией.


Связь с другими вопросами

ВопросТема
201Основная идея continuous batching (этот вопрос — углубление)
840Архитектура инференс-энжинов (vLLM)
841PagedAttention и управление KV-cache (ключевая зависимость)
842Scheduling policies (FCFS, priority, SLO)
845Throughput vs latency trade-offs
846Distributed inference и tensor parallelism

Навигация