Оптимизация инференса LLM с помощью vLLM
vLLM — наиболее популярный open-source движок для высокопроизводительного LLM-инференса. Ключевое нововведение — PagedAttention: управление KV-кешем по аналогии с виртуальной памятью ОС, что устраняет фрагментацию и увеличивает throughput в 15–24 раза по сравнению с наивной реализацией.
Почему стандартный HuggingFace transformers недостаточен
HF transformers хорошо для экспериментов, плохо для production:
- Каждый запрос обрабатывается независимо — нет батчинга запросов
- KV-кеш хранится целиком для каждой последовательности — VRAM тратится неэффективно
- Нет prefill/decode разделения
- Throughput: ~10–50 tokens/sec на одном запросе
vLLM на тех же GPU: 500–2000 tokens/sec через concurrent batching.
Базовое развёртывание vLLM
# Установка
pip install vllm
# Запуск сервера (OpenAI-совместимый API)
python -m vllm.entrypoints.openai.api_server \
--model mistralai/Mistral-7B-Instruct-v0.3 \
--tensor-parallel-size 1 \ # для одной GPU
--max-model-len 8192 \
--max-num-seqs 256 \ # максимальный concurrent batch
--gpu-memory-utilization 0.90 \ # 90% VRAM для модели
--host 0.0.0.0 \
--port 8000
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="none")
response = client.chat.completions.create(
model="mistralai/Mistral-7B-Instruct-v0.3",
messages=[{"role": "user", "content": "Explain transformer attention"}],
max_tokens=500,
temperature=0.7
)
PagedAttention: как это работает
Стандартный KV-кеш: для каждой последовательности выделяется непрерывный блок VRAM на максимальную длину. Фрагментация — между последовательностями теряется до 60% памяти.
PagedAttention: KV-кеш разбивается на страницы фиксированного размера (обычно 16 токенов). Страницы выделяются по требованию, могут быть несмежными. Prefix sharing: если две последовательности имеют общий prefix (system prompt), страницы с prefix'ом используются совместно — экономия VRAM при одинаковых system prompts.
Tensor Parallelism для крупных моделей
# LLaMA-70B на 4xA100 80GB
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3-70b-instruct \
--tensor-parallel-size 4 \ # шардирование по 4 GPU
--dtype bfloat16 \
--max-model-len 16384 \
--gpu-memory-utilization 0.95
Tensor parallelism разбивает матрицы attention heads и FFN по GPU. Для 70B модели: 4xA100 80GB = достаточно при BF16.
Quantization для экономии VRAM
# AWQ квантизация (лучшее качество среди 4-bit методов)
python -m vllm.entrypoints.openai.api_server \
--model TheBloke/Mistral-7B-Instruct-v0.3-AWQ \
--quantization awq \
--dtype auto
# GPTQ
python -m vllm.entrypoints.openai.api_server \
--model TheBloke/Llama-2-13B-GPTQ \
--quantization gptq
# FP8 (для H100)
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3-8b \
--quantization fp8
Результат: 7B модель в AWQ 4-bit занимает ~4 GB VRAM вместо ~14 GB в BF16.
Speculative Decoding
Ускорение декодирования через draft model: маленькая модель (draft) генерирует несколько токенов, большая (target) верифицирует их параллельно. При совпадении — принимаем все токены за один forward pass.
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3-70b-instruct \
--speculative-model meta-llama/Llama-3-8b-instruct \
--num-speculative-tokens 5 \
--tensor-parallel-size 4
Прирост: 1.5–2.5x speedup для типичных текстов при <1% изменении качества.
Тюнинг производительности
# Параметры для максимального throughput (не latency)
VLLM_CONFIG = {
"max_num_seqs": 512, # больше concurrent запросов
"max_num_batched_tokens": 32768, # токены в одном forward pass
"block_size": 32, # размер страницы KV-кеша
"swap_space": 4, # GB для CPU offload при VRAM OOM
}
# Параметры для минимальной latency (не throughput)
VLLM_CONFIG_LATENCY = {
"max_num_seqs": 32,
"max_num_batched_tokens": 4096,
"disable_async_output_proc": False,
}
Бенчмарк производительности
На одном A100 80GB, Mistral-7B-Instruct, 500-токенные ответы:
| Реализация | Throughput (req/s) | P99 Latency |
|---|---|---|
| HF transformers (batch=1) | 1.2 | 8.5s |
| HF transformers (batch=16) | 4.1 | 22s |
| vLLM (256 concurrent) | 28.5 | 12s |
| vLLM + AWQ 4-bit | 52.3 | 7s |







