English translation is not available yet. Showing Russian content.
Почему vLLM быстрее TGI (Hugging Face Text Generation Inference)?
Краткий тезис
vLLM превосходит TGI по скорости в 2–3 раза на больших батчах за счёт трёх ключевых технологий: Attention|Paged Attention, batching|Continuous Batching на уровне итераций и оптимизированных CUDA-ядер. Эти механизмы радикально снижают фрагментацию памяти (с ~70% до ~5%) и позволяют более эффективно утилизировать GPU при обработке запросов разной длины.
1. Терминология: vLLM и TGI
vLLM — open-source библиотека для инференса LLM, разработанная UC Berkeley. Ключевая инновация — Attention|Paged Attention, вдохновлённая механизмом виртуальной памяти в ОС.
TGI (Text Generation Inference) — решение от Hugging Face для деплоя LLM в production. Предоставляет аналогичный функционал: батчирование, управление KV-кэшем, поддержка популярных моделей.
Обе системы решают одну задачу — эффективно обслуживать LLM на GPU, но архитектурные различия дают vLLM значительное преимущество в производительности.
2. Paged Attention — главный фактор ускорения
Paged Attention — это механизм управления KV-кэшем (кэш ключей и значений), разбивающий его на блоки фиксированного размера (страницы). Страницы хранятся в непрерывной памяти GPU, а таблица страниц (page table) отображает логические позиции на физические адреса.
Проблема в TGI (статический KV-кэш)
TGI обычно выделяет непрерывный блок памяти под KV-кэш каждого запроса заранее, ориентируясь на максимальную длину последовательности (max_seq_len). Это приводит к двум проблемам:
- Внутренняя фрагментация если запрос короткий, большая часть выделенной памяти простаивает (~60-70% потери).
- Внешняя фрагментация при освобождении одних запросов и поступлении других память разбивается на маленькие куски, не позволяющие вместить новый запрос без дополнительного копирования.
Решение vLLM: страничная организация
vLLM выделяет страницы (обычно по 16–32 токенов) по мере необходимости. KV-кэш для запроса может храниться в непоследовательных физических страницах. При генерации нового токена система выделяет страницы «на лету». Это снижает фрагментацию до ~5%.
Формула экономии
Эффективность использования памяти ≈ (сумма длин всех последовательностей) / (общий объём памяти, занятой KV-кэшем)
Для TGI с max_seq_len = 4096 и реальной средней длиной последовательности 512 токенов эффективность ~12.5% (при равномерном распределении). Для vLLM с постраничным выделением эффективность может превышать 90%.
Пример кода (концептуально)
# Псевдокод: выделение KV-кэша в TGI (статическое)
def allocate_tgi(batch_size, max_len, hidden_dim):
# Занимает max_len * batch_size * hidden_dim * 2 (K и V) байт
return torch.empty(batch_size, max_len, hidden_dim * 2, device='cuda')
# Псевдокод: vLLM Paged Attention
class PageTable:
def __init__(self, page_size=16, total_pages=1024):
self.page_size = page_size
self.blocks = torch.empty(total_pages, page_size, hidden_dim*2)
self.free_list = list(range(total_pages))
def alloc(self, num_tokens):
pages_needed = (num_tokens + page_size - 1) // page_size
return [self.free_list.pop() for _ in range(pages_needed)]
3. Continuous Batching — агрессивное планирование
Continuous Batching (также «iteration-level scheduling») — это техника, при которой планировщик пересматривает состав батча после каждой итерации (генерации одного токена). В отличие от статического батчирования TGI, где батч фиксируется на весь цикл генерации.
Как работает в TGI (статическое батчирование)
- Все запросы в батче начинают генерацию одновременно.
- Если один запрос завершился раньше (сгенерировал <eos>), освободившееся место не используется до конца батча.
- Новые запросы могут быть добавлены только после полного завершения текущего батча.
- → Простои GPU, низкая утилизация при вариативной длине ответов.
Как работает в vLLM (continuous batching)
- Планировщик после каждого шага декодирования проверяет статус всех активных запросов.
- Завершённые запросы удаляются, на их место сразу добавляются новые из очереди ожидания.
- Батч переформировывается каждую итерацию.
- → GPU постоянно загружен, пропускная способность растёт.
Сравнение
| Параметр | TGI (статическое) | vLLM (continuous) |
|---|---|---|
| Частота обновления батча | Только между раундами генерации | Каждая итерация (каждый токен) |
| Возможность дозагрузки запросов | После полного завершения батча | На каждом шаге |
| Утилизация GPU при разных длинах | Низкая (зависит от самого длинного запроса) | Высокая (адаптивная) |
| Типичный gain по throughput | 1x (база) | 1.5–2.5x |
4. Оптимизированные CUDA-ядра
vLLM поставляется с набором кастомных CUDA kernels, написанных для современных архитектур (A100, H100). Они включают:
- FlashAttention-подобные ядра для эффективного вычисления внимания без материализации полной матрицы внимания.
- PagedAttention kernel — специализированная версия, работающая напрямую со страничным KV-кэшем, избегающая копирования в непрерывный буфер.
- Fused kernels: объединение операций (например, LayerNorm + residual + QKV projection) для уменьшения числа вызовов ядер.
TGI также использует оптимизированные ядра, но они более универсальны (поддержка разных моделей) и не оптимизированы под конкретные страничные структуры. Поскольку vLLM жертвует частью гибкости в пользу скорости, её ядра дают дополнительный выигрыш ~20–30%.
5. Количественное сравнение производительности
| Сценарий | vLLM (токенов/сек) | TGI (токенов/сек) | Ускорение |
|---|---|---|---|
| Одиночный запрос (batch=1) | ~120 | ~110 | ~1.1x |
| Маленький батч (batch=8) | ~400 | ~250 | ~1.6x |
| Средний батч (batch=32) | ~1200 | ~500 | ~2.4x |
| Большой батч (batch=64) | ~1800 | ~600 | ~3.0x |
Цифры приблизительны, на модели Llama-2-7B с H100, при одинаковых max_tokens=512. Источники: бенчмарки сообщества, Lmsys.
Почему разница растёт с размером батча
- Paged Attention сильнее снижает фрагментацию, когда много запросов разной длины — типично для больших батчей.
- Continuous Batching даёт эффект тем больше, чем больше вариативность длин.
- При малых батчах overhead управления страницами может перевешивать выгоду.
6. Дополнительные факторы
- Prefix caching (с версии 0.4): vLLM может кэшировать KV-кэш для общих префиксов (например, системный промпт), экономя prefill-время. TGI поддерживает аналогичную фичу, но реализация менее эффективна.
- Поддержка моделей vLLM быстрее адаптируется к новым архитектурам благодаря модульной системе, но TGI имеет более широкую официальную поддержку (особенно для Hugging Face моделей).
- Memory management vLLM использует асинхронное освобождение страниц и copy-on-write для снижения latency.
7. Когда TGI может быть предпочтительнее?
Несмотря на скорость, TGI имеет свои сильные стороны:
- Простота развёртки — один Docker-контейнер с настройками под большинство моделей.
- Стабильность — Hugging Face активно поддерживает, меньше критических багов.
- Встроенные фичи — встроенные watermarking, детекция токсичности, message API.
- Гибкость — легче кастомизировать генерацию (grammar, JSON-schema).
Если приоритет — максимальная пропускная способность (high-throughput) и вы готовы пожертвовать некоторой гибкостью — выбирайте vLLM. Если нужно быстрое прототипирование или специфические требования — TGI.
Пет-проект для закрепления
Задача Сравнить производительность vLLM и TGI на модели Llama-3.1-8B при различных размерах батча.
Инструменты Docker, Python, Hugging Face Hub, vLLM, TGI с CUDA, библиотека time для замеров.
Шаги:
- Разверните vLLM:
docker run --gpus all -p 8000:8000 vllm/vllm-openai:latest --model meta-llama/Meta-Llama-3.1-8B - Разверните TGI:
docker run --gpus all -p 8080:80 ghcr.io/huggingface/text-generation-inference:latest --model-id meta-llama/Meta-Llama-3.1-8B - Напишите скрипт-бенчмарк:
- Сгенерируйте 100 запросов одинаковой длины (например, 64 токена ввода, 128 токенов вывода).
- Отправляйте запросы с разным concurrency (1, 4, 16, 32).
- Измерьте total time и вычислите throughput (токенов/сек).
- Повторите с разными max_new_tokens и batch sizes.
- Постройте график зависимости throughput от размера батча для обеих систем.
Ожидаемый результат Вы увидите, что при малых батчах разница незначительна, а при больших — vLLM в 2-3 раза быстрее. Также обратите внимание на variance (vLLM обычно более стабильна).
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 830 | Что такое KV-кэш и зачем его оптимизировать? |
| 832 | Как работает FlashAttention? |
| 835 | Что такое prefix caching? |
| 837 | Как выбрать оптимальный batch size? |
| 838 | Что такое speculative decoding? |
Навигация
- Предыдущий: 835
- Следующий: 837
- Индекс: 00. Индекс разборов