Прямой ответ
vLLM — движок инференса LLM, ориентированный на пропускную способность. Два механизма дают основной выигрыш:
- PagedAttention. KV-кэш хранится не непрерывным блоком на запрос, а «страницами», как виртуальная память в ОС. Это убирает фрагментацию: при классическом подходе значительная часть выделенной памяти под кэш простаивает, потому что длина ответа заранее неизвестна.
- Continuous batching (непрерывное батчингирование). Запросы добавляются в выполняющийся батч по мере поступления, а завершившиеся освобождают место немедленно, не дожидаясь конца всей группы. Это резко повышает утилизацию GPU при разнородных запросах.
Практический смысл: на одной и той же видеокарте vLLM обслуживает в разы больше одновременных запросов, чем наивный поштучный инференс, — ценой более сложной настройки и требовательности к VRAM.
Базовый запуск OpenAI-совместимого сервера:
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--dtype auto \
--max-model-len 8192 \
--gpu-memory-utilization 0.90 \
--max-num-seqs 64 \
--port 8000
Ключевые параметры:
--gpu-memory-utilization— доля VRAM, которую движок забирает под веса и KV-кэш. Слишком высокое значение оставляет память под фрагментацию и служебные буферы и приводит к OOM.--max-model-len— максимальная длина контекста (вход + выход). Прямо определяет, сколько страниц кэша нужно на запрос.--max-num-seqs— максимум одновременных последовательностей в батче.--tensor-parallel-size— распараллеливание модели на несколько GPU.
Практика: метрики и настройка под нагрузку
vLLM отдаёт метрики в формате Prometheus на /metrics. Минимально необходимый набор:
- число ожидающих и выполняющихся запросов;
- использование KV-кэша (доля занятых блоков);
- время до первого токена (TTFT) и время на токен (TPOT/ITL);
- количество прерванных (preempted) запросов.
Как интерпретировать:
- KV-кэш заполнен, растёт число preempted. Значит, запросов больше, чем памяти на их контексты. Решения: снизить
--max-model-len, уменьшить--max-num-seqs, добавить GPU или взять квантованную модель. - TTFT растёт, GPU утилизирован не полностью. Часто причина в длинных входных промптах и недостаточном параллелизме префилла; помогает увеличить размер батча и проверить, не упирается ли подача запросов в клиент.
- Утилизация GPU низкая при малой нагрузке. Это норма: continuous batching раскрывается под нагрузкой. Оценивать конфигурацию нужно на целевом RPS, а не на одном запросе.
Рекомендации:
- Измеряйте перцентили, а не среднее. Среднее время ответа маскирует деградацию в хвосте; как строить гистограммы и p95 — в статье про перцентили метрик.
- Разделяйте префилл и генерацию в измерениях. Пользователь воспринимает TTFT как «отвисает», а TPOT — как «медленно печатает». Это разные проблемы с разными решениями.
- Фиксируйте версию движка. Поведение планировщика и метрик меняется между релизами; обновление vLLM стоит проверять на своём нагрузочном профиле.
- Оставляйте запас VRAM. Практика:
--gpu-memory-utilization0.85–0.92 и проверка, что пиковая нагрузка не вызывает OOM.
Что проверять при смене версии движка. Прогоните фиксированный набор запросов и сравните три показателя: долю корректных ответов, p95 времени до первого токена и пропускную способность при целевой конкуренции. Расхождение по качеству означает изменение поведения генерации, а не только производительности, и требует отдельного разбора перед выкладкой.
Практический сценарий: сервис «захлёбывается» в час пик
Контекст: внутренний ассистент на vLLM, один GPU 24 ГБ, модель 7B, --max-model-len 32768. В пике пользователи ждут ответа минутами.
- Диагностика. В метриках: использование KV-кэша 100 %, число preempted запросов растёт, очередь ожидает. Причина — слишком большой заявленный контекст: движок резервирует блоки под 32K токенов на запрос, хотя фактически используются 3–6K.
- Проверка фактических длин. По логам запросов: p95 входа — 2.4K токенов, p95 выхода — 400 токенов.
- Исправление. Ставим
--max-model-len 8192, увеличиваем--max-num-seqsдо 96, оставляем--gpu-memory-utilization 0.90. На стороне клиента добавляем обрезку истории диалога. - Результат. Одновременных запросов обслуживается втрое больше, p95 TTFT падает с 40 с до 3 с, preempted исчезают.
- Профилактика. Добавляем алерты на заполнение KV-кэша выше 90 % и на рост preempted; в runbook фиксируем процедуру снижения лимитов — как оформлять такие инструкции, см. incident management и runbook.
Вывод: в vLLM пропускная способность определяется памятью под KV-кэш, а она расходуется пропорционально заявленной длине контекста. Занижать фактические потребности так же вредно, как завышать лимиты.
FAQ
Когда vLLM избыточен и достаточно llama.cpp? Для одного-двух пользователей и локальной разработки достаточно llama-server. vLLM оправдан при десятках одновременных запросов, когда важна пропускная способность на единицу стоимости GPU.
Поддерживает ли vLLM квантованные модели? Да: AWQ, GPTQ, FP8 и другие схемы, в зависимости от версии движка. Квантование уменьшает объём весов и освобождает VRAM под KV-кэш, что напрямую увеличивает допустимый параллелизм.
Что такое preempted запросы? Когда памяти на KV-кэш не хватает, планировщик вытесняет часть последовательностей, чтобы продолжить другие. Вытесненный запрос либо пересчитывается позже, либо восстанавливается из выгруженного кэша. Рост числа вытеснений — явный признак нехватки памяти под контексты.
Как масштабировать дальше одной видеокарты?
Варианты: --tensor-parallel-size для разделения одной модели на несколько GPU, либо несколько независимых инстансов за балансировщиком. Второй вариант проще эксплуатировать и даёт предсказуемую деградацию при отказе одного инстанса; оценку требуемого числа инстансов делают через capacity planning.
Источники: vLLM docs, PagedAttention paper, vLLM OpenAI server docs — ссылки в шапке.