Как вы деплоите LLM с requirement <100ms latency при throughput 1000 req/s? Архитектура.
Краткий тезис
Деплой LLM с latency <100ms и throughput 1000 req/s требует многоуровневой архитектуры: L7-балансировщик распределяет запросы между несколькими репликами vLLM, каждая из которых использует адаптивный батчинг и спекулятивное декодирование с маленькой draft-моделью. Redis выступает распределённым KV-cache для ускорения повторяющихся запросов. Аппаратная основа — NVIDIA H100 или B200 с оптимизированной памятью и межсоединениями. Ключевые метрики — P99 latency и batch size — мониторятся для автоскейлинга.
1. Термины и требования
Latency — время от отправки запроса до получения первого токена ответа (TTFT) или полного ответа. Для <100ms обычно речь о TTFT (time to first token). Throughput — количество обработанных запросов в секунду (req/s). 1000 req/s — высоконагруженный сценарий (например, чат-бот для миллионов пользователей).
P99 latency — 99-й перцентиль задержки: 99% запросов должны укладываться в 100ms. Это жёстче среднего.
Batch size — количество запросов, обрабатываемых моделью за один проход. Чем больше batch, тем выше throughput, но растёт latency из-за ожидания накопления батча.
KV-cache — кэш ключей и значений внимания, ускоряющий генерацию повторяющихся префиксов (например, системный промпт). Speculative decoding — техника, при которой маленькая draft-модель генерирует несколько токенов, а model|большая модель проверяет их, экономя время на последовательных forward-проходах.
2. Балансировка запросов: L7 Load Balancer
Первый уровень — L7 (application layer) балансировщик (например, NGINX, Envoy, HAProxy). Он распределяет входящие HTTP/gRPC запросы по нескольким репликам vLLM на основе round-robin или least connections. Для минимизации latency балансировщик должен быть географически близок к клиентам и использовать keep-alive соединения.
Конфигурация NGINX (фрагмент):
upstream llm_backend {
least_conn;
server 10.0.1.1:8000 max_fails=3 fail_timeout=30s;
server 10.0.1.2:8000;
server 10.0.1.3:8000;
}
server {
listen 443 ssl http2;
location /v1/chat/completions {
proxy_pass http://llm_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
Health checks — балансировщик периодически проверяет /health эндпоинт каждой реплики и исключает нездоровые.
3. Инференс-сервер: vLLM с адаптивным батчингом
vLLM — open-source библиотека для эффективного инференса LLM. Ключевые фичи:
- PagedAttention — эффективное управление KV-cache, уменьшающее фрагментацию памяти.
- Continuous batching (адаптивный батчинг) — запросы добавляются в батч по мере поступления, не дожидаясь заполнения фиксированного размера. Это снижает latency для ранних запросов.
Пример запуска vLLM с оптимизациями:
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Meta-Llama-3-70B \
--tensor-parallel-size 4 \
--pipeline-parallel-size 2 \
--max-num-seqs 256 \
--max-model-len 4096 \
--gpu-memory-utilization 0.95 \
--enable-chunked-prefill \
--speculative-model meta-llama/Llama-3.2-1B \
--num-speculative-tokens 5
Параметры:
- tensor-parallel-size — число GPU для тензорного параллелизма (разделение слоёв).
pipeline-parallel-size— число GPU для конвейерного параллелизма (разделение по слоям).- max-num-seqs — максимальный размер батча.
enable-chunked-prefill— разбивает префилл (обработку промпта) на чанки, уменьшая TTFT.speculative-model— путь к draft-модели для спекулятивного декодирования.
Адаптивный батчинг динамически выбирает размер батча на основе текущей загрузки. При низкой нагрузке батчи маленькие (низкая latency), при высокой — большие (высокий throughput). vLLM использует планировщик, который максимизирует throughput при соблюдении SLA по latency.
4. Распределённый KV-cache: Redis
Для запросов с одинаковыми префиксами (например, системный промпт, история диалога) можно кэшировать KV-cache в Redis (или Memcached). Это сокращает время префилла для повторяющихся контекстов.
Архитектура:
- Каждый запрос содержит hash префикса (например, SHA256 первых N токенов).
- vLLM перед префиллом проверяет Redis: если ключ существует, загружает готовый KV-cache и достраивает только новые токены.
- TTL для кэша — несколько минут (зависит от частоты обновления контекста).
Пример Redis-команды:
import redis
r = redis.Redis(host='redis-cluster', port=6379, decode_responses=True)
cache_key = f"kv_cache:{hash_prefix}"
cached = r.get(cache_key)
if cached:
# загрузить в GPU memory
pass
else:
# вычислить и сохранить
r.setex(cache_key, 300, serialized_cache)
Проблемы:
- Сериализация/десериализация KV-cache (размер может быть ~1-10 MB на запрос) — требует быстрой сети (RDMA).
- Синхронизация при обновлении модели — инвалидация кэша.
5. Speculative Decoding
Speculative decoding ускоряет генерацию, используя маленькую draft-модель (например, 1B параметров) для быстрой генерации K токенов, а большая модель (70B) проверяет их параллельно. Если draft-модель угадала, мы получаем K токенов за один forward большой модели. Типичное ускорение — 2-3x.
Процесс:
- Draft-модель генерирует K токенов (например, 5) авторегрессивно.
- Большая модель делает один forward на всех K+1 позициях (включая исходный токен).
- Сравниваются распределения: если вероятность draft-токена выше порога, он принимается; иначе — rejection sampling.
- Принятые токены возвращаются, rejected — заменяются на токен большой модели.
Выбор draft-модели:
- Должна быть достаточно маленькой (1-3B), чтобы latency её генерации была мала.
- Должна иметь высокий acceptance rate (доля принятых токенов) — обычно >0.7 для близких по домену моделей.
- Можно использовать Medusa (дополнительные головы на большой модели) вместо отдельной draft-модели.
6. Аппаратное обеспечение: H100 vs B200
| Параметр | NVIDIA H100 SXM | NVIDIA B200 |
|---|---|---|
| FP8 TFLOPS | 1979 | 4500 |
| HBM3 память | 80 GB | 192 GB |
| Пропускная способность памяти | 3.35 TB/s | 8 TB/s |
| NVLink | 900 GB/s | 1800 GB/s |
| TDP | 700 W | 1000 W |
| Цена (ориентир) | ~$30k | ~$50k |
Для 1000 req/s с latency <100ms потребуется кластер из нескольких GPU. Пример расчёта для Llama-3-70B:
- Одна H100 обрабатывает ~50 req/s (с speculative decoding и batch=64).
- Для 1000 req/s нужно ~20 H100 (5 узлов по 4 GPU).
- B200 даёт ~2x больше throughput за счёт большей памяти и пропускной способности, поэтому хватит 10 B200.
Конфигурация узла:
- 4-8 GPU H100/B200 с NVLink.
- CPU: AMD EPYC или Intel Xeon с высокими частотами.
- RAM: 512 GB+ для хранения модели и KV-cache.
- Сеть: InfiniBand HDR (200 Gbps) или NVIDIA Spectrum-X для межузлового общения.
7. Мониторинг и автоскейлинг
Ключевые метрики:
- P99 latency (должен быть <100ms).
- Throughput (req/s).
- GPU utilization (цель >80%).
- Batch size (средний и максимальный).
- KV-cache hit ratio (для Redis).
Инструменты:
- Prometheus + Grafana для сбора и визуализации.
- Kubernetes с Horizontal Pod Autoscaler (HPA) на основе CPU/memory или custom metrics (latency).
- Alertmanager для оповещений при превышении latency.
Пример HPA на основе Prometheus:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm
minReplicas: 5
maxReplicas: 20
metrics:
- type: Pods
pods:
metric:
name: p99_latency_ms
target:
type: AverageValue
averageValue: 80
8. Оптимизации и компромиссы
| Техника | Влияние на latency | Влияние на throughput | Сложность |
|---|---|---|---|
| Адаптивный батчинг | - (увеличивает при высокой нагрузке) | + | Низкая |
| Speculative decoding | -- (снижает) | + | Средняя |
| KV-cache (Redis) | -- (снижает TTFT) | + (при hit) | Высокая |
| Tensor parallelism | - (снижает за счёт коммуникации) | ++ | Средняя |
| Pipeline parallelism | - (снижает) | + | Средняя |
| Chunked prefill | -- (снижает TTFT) | 0 | Низкая |
Компромисс: увеличение batch size повышает throughput, но может поднять P99 latency выше 100ms. Необходимо найти оптимальный max-num-seqs через нагрузочное тестирование.
9. Пример архитектуры (диаграмма текстом)
Client → L7 LB (NGINX) → vLLM Replica 1 (GPU: H100x4)
→ vLLM Replica 2 (GPU: H100x4)
→ vLLM Replica 3 (GPU: H100x4)
→ vLLM Replica 4 (GPU: H100x4)
→ vLLM Replica 5 (GPU: H100x4)
|
├── Redis Cluster (KV-cache)
└── Monitoring (Prometheus + Grafana)
Каждая реплика vLLM использует speculative decoding с draft-моделью (загружена на те же GPU или отдельные). Redis кластер из 3-5 нод для отказоустойчивости.
Пет-проект для закрепления
Задача: Развернуть Llama-3-8B с latency <50ms и throughput 100 req/s на одной H100 (или эмулировать на CPU с меньшими требованиями).
Инструменты: Docker, vLLM, Redis, Locust (для нагрузочного тестирования), Prometheus + Grafana.
Шаги:
- Запустить vLLM с моделью Llama-3-8B, включив continuous batching и chunked prefill.
- Настроить Redis и модифицировать vLLM (или использовать встроенный prefix caching) для кэширования KV-cache.
- Написать скрипт на Python с использованием
openaiклиента для отправки запросов с разными префиксами. - Запустить Locust с 50 concurrent users, измеряя latency и throughput.
- Настроить Prometheus для сбора метрик vLLM (экспортёр уже встроен) и визуализировать в Grafana.
- Поэкспериментировать с
max-num-seqsиnum-speculative-tokens, найти оптимальные значения.
Ожидаемый результат: Достичь P99 latency <50ms при 100 req/s, продемонстрировать снижение TTFT при использовании KV-cache (hit ratio >50%).
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 7 | Как вы уменьшаете latency RAG-системы? |
| 10 | Что такое Self-RAG и когда его использовать? |
| 1 | Как бы вы спроектировали RAG-систему для 10 000 документов? |
| 9 | Как вы обновляете документы в существующей RAG-системе? |
| 2 | Как вы решаете проблему lost in the middle? |
| 3 | Какие стратегии chunking'а вы знаете? |
Навигация
- Предыдущий: 204
- Следующий: 206
- Индекс: 00. Индекс разборов