Если ваш LLM-сервис испытывает высокую нагрузку при большом числе одновременных пользователей, каждый запрос обрабатывается последовательно — без батчинга throughput падает в разы, а latency растёт до неприемлемых значений. Мы настраиваем dynamic batching, чтобы GPU работала на 80%+ утилизации, а не на 5%. Наши инженеры имеют более 4 лет опыта в продакшене LLM и реализовали более 20 проектов на vLLM, TensorRT-LLM и кастомных решениях. Dynamic batching объединяет несколько параллельных запросов в один forward pass через GPU. Это ключевой механизм для высокого throughput LLM: GPU параллелен и обрабатывает матричные умножения эффективнее для больших батчей. Правильная настройка батчинга позволяет сократить количество необходимых GPU в 3-5 раз, что экономит от $1.5k–5k в месяц на инфраструктуре.
Почему батчинг критичен для LLM?
Без батчинга даже мощная GPU A100 80GB выдает лишь 30 tokens/sec для модели Llama-3-8B. При batch=16 – 300 tokens/sec, а при batch=64 – уже 900 tokens/sec. Таким образом, прирост в 30 раз. Однако latency p99 растет с 200 мс до 400 мс, что все еще приемлемо для большинства real-time сценариев. Если у вас 100 concurrent пользователей, без батчинга каждый будет ждать своей очереди – общее время ответа может превысить минуту. С continuous batching все запросы обрабатываются параллельно, и время ответа снижается до секунд.
| Batch size | Throughput (tokens/sec) | Latency p99 (ms) | GPU Utilization |
|---|---|---|---|
| 1 | 30 | 200 | 15% |
| 16 | 300 | 250 | 65% |
| 64 | 900 | 400 | 90% |
Почему continuous batching выигрывает у статического?
Static batching фиксирует размер батча и ждёт его заполнения, что увеличивает latency при низкой нагрузке. Continuous batching (in-flight batching) добавляет запросы в батч динамически — как только GPU освобождается, он сразу обрабатывает следующую пачку. Это снижает время ожидания и повышает utilisation.
| Тип батчинга | Размер батча | Время ожидания | Throughput | GPU Utilization |
|---|---|---|---|---|
| Static | Фиксированный | Высокое при низкой нагрузке | Средний | Низкий |
| Dynamic | Адаптивный | Среднее | Высокий | Средний |
| Continuous | Адаптивный, in-flight | Низкое | Очень высокий | Высокий |
Continuous (In-flight) Batching в vLLM
Согласно официальной документации vLLM, continuous batching реализован автоматически. Ключевые параметры: max-num-seqs — максимальное число запросов в батче, max-num-batched-tokens — общее количество токенов в батче, scheduler-delay-factor — задержка перед формированием батча. Пример конфигурации:
python -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-3-8b-instruct \ --max-num-seqs 256 \ --max-num-batched-tokens 32768 \ --scheduler-delay-factor 0.5 \ --use-v2-block-manager \ --enable-chunked-prefill Chunked prefill разбивает длинный prefill на чанки, что не блокирует decode других запросов:
--enable-chunked-prefill --max-num-batched-tokens 8192 Как настроить dynamic batching под конкретную GPU?
Следуйте этим шагам:
- Определите модель и GPU. Например, Llama-3-8B на A100-80GB.
- Выберите фреймворк. vLLM — для быстрого старта, TensorRT-LLM — для максимальной производительности.
- Запустите бенчмаркинг. Используйте нагрузочные тесты с разным числом concurrent пользователей.
- Настройте параметры.
max-num-seqs,max-num-batched-tokens,scheduler-delay-factor. - Мониторинг. Отслеживайте метрики
num_requests_running,avg_prompt_throughput_toks_per_s.
Какие метрики мониторинга важны для батчинга?
vLLM экспортирует метрики через Prometheus: num_requests_running (запросы в активном батче), num_requests_waiting (в очереди), avg_prompt_throughput_toks_per_s, avg_generation_throughput_toks_per_s. С помощью этих метрик можно настроить баланс между throughput и latency. Для комплексного мониторинга используйте Grafana.
Типичные ошибки при настройке батчинга:
- Слишком большой max-num-seqs: ведёт к росту latency p99 из-за конкуренции за память KV cache.
- Игнорирование chunked prefill: длинные промпты блокируют decode, снижая utilisation.
- Отсутствие бенчмаркинга под реальную нагрузку: параметры, подобранные на синтетических данных, часто не работают в продакшене.
Настройка динамического батчинга в TensorRT-LLM / Triton
# tensorrt_llm/config.pbtxt parameters { key: "max_tokens_in_paged_kv_cache" value: { string_value: "40000" } } parameters { key: "batch_scheduler_policy" value: { string_value: "guaranteed_no_evict" } } parameters { key: "executor_static_batch_size" value: { string_value: "-1" } } Ручная реализация батчинга (пример DynamicBatchInferenceServer)
Если используется собственный inference server:
import asyncio from dataclasses import dataclass from collections import deque import time @dataclass class PendingRequest: id: str prompt: str max_tokens: int future: asyncio.Future enqueued_at: float class DynamicBatchInferenceServer: def __init__( self, model, max_batch_size: int = 64, max_wait_ms: float = 20.0, max_tokens_per_batch: int = 16384 ): self.model = model self.max_batch_size = max_batch_size self.max_wait_ms = max_wait_ms self.max_tokens_per_batch = max_tokens_per_batch self.queue: deque[PendingRequest] = deque() self.lock = asyncio.Lock() self._batch_worker_task = None async def start(self): self._batch_worker_task = asyncio.create_task(self._batch_worker()) async def predict(self, prompt: str, max_tokens: int = 512) -> str: future = asyncio.get_event_loop().create_future() request = PendingRequest( id=str(time.time()), prompt=prompt, max_tokens=max_tokens, future=future, enqueued_at=time.time() ) async with self.lock: self.queue.append(request) return await future async def _batch_worker(self): while True: await asyncio.sleep(self.max_wait_ms / 1000) async with self.lock: if not self.queue: continue batch: list[PendingRequest] = [] total_tokens = 0 while (self.queue and len(batch) < self.max_batch_size and total_tokens + self.queue[0].max_tokens <= self.max_tokens_per_batch): req = self.queue.popleft() batch.append(req) total_tokens += len(req.prompt.split()) + req.max_tokens if not batch: continue prompts = [req.prompt for req in batch] max_tokens_list = [req.max_tokens for req in batch] try: outputs = self.model.generate_batch(prompts, max(max_tokens_list)) for req, output in zip(batch, outputs): if not req.future.done(): req.future.set_result(output) except Exception as e: for req in batch: if not req.future.done(): req.future.set_exception(e) Кейс: оптимизация для высоконагруженного чат-бота
Клиент с нагрузкой 2000 запросов в минуту использовал 8 GPU A100 без батчинга. После настройки continuous batching с параметрами max-num-seqs=256 и chunked prefill удалось обрабатывать ту же нагрузку на 2 GPU. Экономия инфраструктуры составила $3.6k–5.2k в месяц. Окупаемость проекта — 3 недели.Благодаря настройке dynamic batching наши клиенты сокращают затраты на GPU-инфраструктуру в 3-10 раз, добиваясь окупаемости проекта в течение 2-3 месяцев. Экономия составляет от $1.4k–1.9k в месяц.
Что входит в настройку
- Конфигурация inference server (vLLM, TensorRT-LLM или кастомный)
- Бенчмаркинг и подбор параметров батчинга
- Интеграция мониторинга метрик батчинга
- Документация по развёртыванию и поддержке
- Обучение команды (опционально)
Ориентировочные сроки: от 2 до 10 рабочих дней в зависимости от сложности. Стоимость рассчитывается индивидуально.
Получите консультацию по оптимизации throughput вашего LLM. Свяжитесь — оценим проект за 1 день. Закажите аудит текущей конфигурации батчинга — мы выявим узкие места и предложим улучшения с расчетом экономии.







