Continuous Batching для LLM: как работает, почему ускоряет инференс в 10 раз | AiManual
AiManual Logo Ai / Manual.
07 Янв 2026 Гайд

Continuous Batching: как устроен ключевой механизм для высокой нагрузки LLM-сервисов

Глубокое техническое объяснение Continuous Batching — механизма, который позволяет обрабатывать тысячи запросов к LLM одновременно. От основ внимания до практич

Когда токен за токеном превращается в финансовую катастрофу

Представьте себе сервис типа ChatGPT, который обрабатывает 10 000 запросов в секунду. Каждый запрос генерирует ответ по 100 токенов. Стандартный подход — обрабатывать запросы батчами. Вы собираете N запросов, прогоняете их через модель одновременно, ждете, пока все закончат генерацию, и только потом берете следующую партию.

Звучит логично? А теперь посчитайте. Пользователь А запросил перевод "Привет" на английский — модель выдаст "Hello" за один шаг. Пользователь Б попросил составить бизнес-план на 10 страниц — это 2000 токенов генерации. Пользователь А давно получил ответ и ушел пить кофе, а вы продолжаете ждать, пока модель закончит генерировать бизнес-план для пользователя Б, прежде чем обработать новые запросы.

Вот она — главная проблема статического батчинга: GPU простаивает 95% времени, пока ждет самого длинного запроса в батче. Вы платите за дорогой A100, который большую часть времени просто греет воздух в дата-центре.

Ключ к пониманию — как LLM на самом деле генерируют текст

Чтобы понять, почему Continuous Batching работает, нужно разобраться с фундаментальным ограничением трансформеров. LLM не могут "заглянуть вперед" или сгенерировать ответ сразу целиком. Они работают итеративно: принимают последовательность токенов (промпт), вычисляют распределение вероятностей для следующего токена, выбирают наиболее вероятный (или не совсем), добавляют его к последовательности и повторяют.

Каждая итерация требует полного прохода через все слои модели. И вот здесь начинается магия оптимизации.

💡
Механизм внимания (attention) в трансформерах — это не просто "взвешенная сумма". Для генерации каждого нового токена модель должна вычислить attention-веса между всеми токенами в последовательности, включая только что сгенерированные. Эти вычисления составляют львиную долю вычислительной нагрузки.

KV Cache: почему не нужно пересчитывать всё каждый раз

Самое дорогое в attention — умножение матриц Query на Key для получения весов. Но ключевое наблюдение: для уже обработанных токенов значения Key и Value (KV) не меняются от итерации к итерации. Токен "При" в промпте "Привет мир" имеет одни и те же KV на первом, втором и сотом шаге генерации.

Инженеры догадались кэшировать эти KV пары. Вместо пересчета attention с нуля для всей последовательности, модель просто добавляет KV для нового токена в кэш и вычисляет attention только между Query нового токена и всеми Key в кэше.

ПодходВычисления на токенПамятьКогда использовать
Без KV CacheO(n²) для n токеновМинимумНикогда в продакшене
С KV CacheO(n) для n токеновРастет линейноВсегда для инференса

Continuous Batching: как заставить GPU работать на 100%

Теперь возвращаемся к нашей проблеме простаивающего GPU. Continuous Batching (он же iteration-level scheduling или dynamic batching) ломает парадигму "обработал батч — освободился".

Вместо того чтобы ждать, пока все запросы в батче закончат генерацию, система обрабатывает один шаг (одну итерацию) для всех активных запросов, а затем сразу переходит к следующему шагу. Запросы, которые завершили генерацию (выдали end-of-sequence токен), удаляются из батча. На их место добавляются новые запросы из очереди.

1Как это выглядит на практике

Допустим, у вас есть 4 запроса в системе:

  • Запрос А: "Переведи 'кот' на английский" (ожидаемая длина: 1 токен)
  • Запрос Б: "Напиши стихотворение про весну" (ожидаемая длина: 50 токенов)
  • Запрос В: "Объясни квантовую механику" (ожидаемая длина: 200 токенов)
  • Запрос Г: "Что такое API?" (ожидаемая длина: 10 токенов)

В статическом батчинге вы бы обработали все 4 запроса одновременно. Запрос А завершится на первом шаге, но GPU будет ждать, пока запрос В не сгенерирует все 200 токенов.

В Continuous Batching после первого шага запрос А завершается и освобождает слот в батче. Система немедленно берет из очереди новый запрос Д и начинает его обрабатывать на втором шаге вместе с Б, В и Г.

💡
Это похоже на конвейер на фабрике. Детали (запросы) движутся по конвейеру (шаги генерации). Как только деталь готова (запрос завершен), ее снимают с конвейера и ставят новую. Конвейер никогда не останавливается.

2Технические детали реализации

Самое сложное в Continuous Batching — управление памятью и вычислениями для запросов разной длины. Каждый запрос имеет свой KV Cache разного размера. Нужно как-то упаковать все это в тензоры, которые можно эффективно обрабатывать на GPU.

Современные системы (vLLM, TGI от Hugging Face) используют paged attention — аналог виртуальной памяти для KV Cache. KV Cache разбивается на блоки фиксированного размера (например, 128 токенов). Каждый запрос получает столько блоков, сколько нужно. Блоки могут быть несмежными в памяти, как страницы в оперативной памяти компьютера.

На каждом шаге система строит "таблицу страниц", которая указывает, какие блоки KV Cache соответствуют каким позициям в каких запросах. Attention механизм использует эту таблицу для правильного доступа к данным.

Что ломается при переходе на Continuous Batching

В теории все прекрасно. На практике возникает дюжина подводных камней.

Самая болезненная проблема — contention на памяти. Когда десятки запросов одновременно пытаются аллоцировать и освобождать блоки KV Cache, возникает классическая ситуация race condition. Нужна сложная система блокировок или lock-free аллокатор.

Проблема 1: Разная длина промптов

Запросы приходят с промптами разной длины. Некоторые системы предварительно вычисляют KV Cache для всего промпта перед началом генерации. Это создает пиковую нагрузку на память в момент добавления нового запроса в батч.

Решение — incremental encoding: вычислять KV Cache для промпта по мере продвижения по шагам генерации. Но это усложняет логику, потому что нужно отслеживать, для каких токенов промпта KV уже вычислены, а для каких нет.

Проблема 2: Прерывание генерации

Пользователь нажал "Стоп" посреди генерации длинного ответа. Нужно немедленно освободить все блоки KV Cache, которые занимал этот запрос, и убрать его из батча. Если делать это синхронно, можно заблокировать весь конвейер.

Правильное решение — асинхронное освобождение памяти с фоновым garbage collector. Запрос помечается как отмененный, но физическое освобождение блоков происходит позже, когда система не занята критическими вычислениями.

Проблема 3: Приоритеты и QoS

Не все запросы равны. Премиум-пользователи ожидают низкую задержку. Research-запросы могут подождать. В статическом батчинге вы просто создаете отдельные очереди. В Continuous Batching приоритеты влияют на scheduling: какой запрос взять следующим, когда освободится слот?

Некоторые системы реализуют weighted fair queueing: каждый запрос получает виртуальное время старта с поправкой на приоритет. Запросы с высоким приоритетом "опережают" время.

Реальные цифры: во сколько раз ускоряется обработка

Без Continuous Batching utilization GPU редко превышает 20-30% при смешанной нагрузке (короткие и длинные запросы). С Continuous Batching можно достичь 70-80% utilization.

МетрикаСтатический батчингContinuous BatchingУлучшение
Токенов в секунду120058004.8x
Запросов в секунду452104.7x
P99 latency850 мс180 мс4.7x лучше

Цифры из тестов vLLM на LLaMA-13B с A100. Важно: улучшение зависит от распределения длин запросов. Если все запросы одинаковой длины, Continuous Batching не дает преимущества. В реальном мире такое почти никогда не случается.

Как внедрить Continuous Batching в свой проект

Писать свою реализацию с нуля — безумие. Есть три практических пути:

  1. Использовать vLLM — сегодняшний золотой стандарт. Поддерживает большинство популярных моделей, имеет встроенную систему serving с API OpenAI-совместимого формата. Из коробки дает Continuous Batching с paged attention.
  2. Text Generation Inference (TGI) от Hugging Face — больше ориентирован на их экосистему. Хорошо интегрируется с трансформерами, поддерживает quantization.
  3. Использовать специализированные фреймворки вроде TensorRT-LLM от NVIDIA или MLC для edge-деплоя.

Если вы разворачиваете модель на своем железе и ожидаете высокую нагрузку, vLLM — самый безопасный выбор. Он абстрагирует всю сложность Continuous Batching за простым API.

Важный нюанс: Continuous Batching требует больше памяти, чем статический батчинг. Нужно резервировать память под рост KV Cache для новых запросов, которые могут прийти в любой момент. Настройте memory limits правильно.

Интеграция с существующей инфраструктурой

Допустим, у вас уже есть очередь запросов и система балансировки нагрузки. Добавление Continuous Batching поверх — это не просто замена библиотеки.

Нужно пересмотреть:

  • Мониторинг: традиционные метрики вроде "запросов в секунду" становятся менее информативными. Важнее "токенов в секунду" и "utilization GPU"
  • Autoscaling: как определять, когда добавлять новые инстансы? По средней длине очереди? По времени ожидания P95?
  • Rate limiting: ограничивать запросы в секунду или токены в секунду? Второе честнее, но сложнее реализовать.

Что будет дальше: эволюция батчинга

Continuous Batching — не конечная точка оптимизации. Уже появляются более агрессивные техники:

Predictive batching: система пытается предсказать длину ответа на основе промпта и распределяет запросы по батчам так, чтобы они завершались примерно одновременно. Машинное обучение для scheduling.

Selective batching: не все слои модели одинаково важны для разных типов запросов. Можно пропускать некоторые attention heads или даже целые слои для "простых" запросов. Особенно актуально для mixture-of-experts моделей.

Cross-request optimization: если в батче несколько запросов с похожими промптами, можно частично переиспользовать вычисления между ними. Как кластеризация промптов, но на уровне отдельных матричных умножений.

Самый радикальный подход — полностью асинхронная генерация, где каждый запрос живет в своем собственном временном потоке, а GPU обрабатывает микробатчики из одного токена от сотен разных запросов. Технически реализовать почти невозможно, но исследования в этом направлении уже ведутся.

💡
Если сегодня вы запускаете LLM сервис без Continuous Batching, вы платите за железо в 4-5 раз больше, чем нужно. Это не оптимизация — это базовое требование для любого продакшен-деплоя после 2023 года.

Начните с vLLM. Протестируйте на своей нагрузке. Посмотрите на метрики. И приготовьтесь объяснять CFO, почему теперь можно обслуживать в 5 раз больше пользователей на тех же самых GPU.