Развернуть vLLM против TGI и сравнить throughput
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Развернуть vLLM против TGI и сравнить throughput
1. Цель задачи
Практически сравнить два популярных inference-сервера для LLM — vLLM и Text Generation Inference (TGI) — на одинаковой модели и одинаковом железе. Измерить throughput (токенов/сек) и latency при идентичной нагрузке. Определить, насколько vLLM быстрее TGI в условиях production-подобного бенчмарка (целевое ускорение 20–50%).
Ключевой результат Два контейнерных развёртывания модели (vLLM и TGI), воспроизводимый бенчмарк-скрипт и сводный отчёт с графиками и численными метриками, подтверждающий/опровергающий гипотезу ускорения.
2. Исходные данные
Перед началом необходимо иметь:
| Что нужно | Откуда взять |
|---|---|
Модель (например, meta-llama/Llama-2-7b-chat-hf) | Hugging Face Hub (предварительно получить доступ) |
| GPU-инстанс (минимум 16 GB VRAM, например, NVIDIA T4, A10G, RTX 4090) | Облачный провайдер (AWS, GCP, Lambda Labs) или локальная машина |
| Docker + NVIDIA Container Toolkit | Официальная документация NVIDIA |
| Python 3.10+ | Установленный на хосте |
Утилиты для нагрузки (ab, locust или собственный скрипт) | Стандартные пакеты / pip |
Если нет реального GPU — симулируем:
- Заменить модель на меньшую (например,
TinyLlama/TinyLlama-1.1B-Chat-v0.6) — влезет в 2 GB VRAM и даст корректный паттерн сравнения. - Использовать CPU-режим (не рекомендуется, т.к. throughput будет несопоставимо низким и не отразит разницу между серверами). Лучше взять маленький GPU-инстанс (T4 от Google Colab — бесплатно, но лимит по времени).
- Запускать оба сервера последовательно на одном и том же GPU, фиксировать util GPU через nvidia-smi для честности.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Inference сервер #1 | vLLM (контейнер vllm/vllm-openai:latest) | Serving модели с PagedAttention |
| Inference сервер #2 | TGI (контейнер ghcr.io/huggingface/text-generation-inference:latest) | Serving модели с continuous batching |
| Модель | Hugging Face Llama-2-7b-chat-hf | Целевая LLM |
| Бенчмарк | Python (aiohttp + asyncio) + curl | Генерация синтетической нагрузки |
| Мониторинг | nvidia-smi, docker stats, Prometheus (опционально) | Загрузка GPU, память, время |
| Виртуальная среда | Docker Compose (опционально) | Оркестрация контейнеров |
| Отчёт | Jupyter Notebook / Markdown + Plotly | Визуализация результатов |
4. Этапы выполнения
Этап 1: Подготовка окружения и развёртывание vLLM (2–3 часа)
Действия
-
Установить Docker и NVIDIA Container Toolkit (если не установлено).
Проверить: sudo docker run --rm --gpus all nvidia/cuda:11.8-base nvidia-smi -
Создать директорию проекта и файл .env с переменными:
MODEL_NAME=meta-llama/Llama-2-7b-chat-hf
HF_TOKEN=<ваш токен> (только для gated моделей) -
Запустить контейнер vLLM с OpenAPI-совместимым эндпоинтом:
docker run --gpus all \ -p 8000:8000 \ -e HF_TOKEN=$HF_TOKEN \ vllm/vllm-openai:latest \ --model $MODEL_NAME \ --max-model-len 4096 \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.90Примечание: Если GPU мало, уменьшить
--max-model-lenдо 2048. -
Проверить, что сервер отвечает:
curl -X POST http://localhost:8000/v1/completions \ -H "Content-Type: application/json" \ -d '{"model": "meta-llama/Llama-2-7b-chat-hf", "prompt": "Hello", "max_tokens": 50}'
Ожидаемый результат этапа
Контейнер vLLM запущен, модель загружена, API отвечает на запросы.
Этап 2: Развёртывание TGI (1.5–2 часа)
Действия
-
Запустить контейнер TGI на порту 8080 (чтобы не конфликтовать):
docker run --gpus all \ -p 8080:80 \ -e HF_TOKEN=$HF_TOKEN \ ghcr.io/huggingface/text-generation-inference:latest \ --model-id $MODEL_NAME \ --max-total-tokens 4096 \ --max-input-length 1024 \ --max-batch-prefill-tokens 4096 -
Проверить TGI (эндпоинт
/generate):curl -X POST http://localhost:8080/generate \ -H "Content-Type: application/json" \ -d '{"inputs": "Hello", "parameters": {"max_new_tokens": 50}}' -
Убедиться, что обе модели работают на одном GPU (не одновременно).
Для честности перед каждым тестом перезапускать второй контейнер, чтобы освободить VRAM.
Ожидаемый результат этапа
Контейнер TGI работает, API отвечает корректно.
Этап 3: Написание бенчмарк-скрипта (1.5–2 часа)
Действия
-
Создать Python скрипт benchmark.py с функциями:
- warmup(server_url, model, n_requests=5)
- run_load(server_url, model, prompts, max_tokens, concurrency=4, total_requests=50)
-
Использовать aiohttp для асинхронных запросов:
async def send_request(session, url, payload): async with session.post(url, json=payload) as resp: return await resp.json(), resp.status -
Фиксировать метрики:
- Latency каждого запроса (time to first token + полное время)
- Throughput (количество сгенерированных токенов / общее время)
- Использование GPU (через nvidia-smi --query-gpu=utilization.gpu,memory.used --format=csv -l 1 в параллельном процессе)
-
Подготовить набор промптов (50–100 штук) фиксированной длины (например, 128 токенов) и запросить генерацию 256 токенов.
Промпты можно взять из датасета wikitext или сгенерировать случайно. -
Запустить скрипт последовательно для vLLM и TGI (с перезапуском контейнера между тестами).
Ожидаемый результат этапа
Скрипт выдаёт CSV-файл с колонками: server, request_id, prompt_len, gen_len, latency_sec, ttft_sec, gpu_util, gpu_mem.
Этап 4: Сбор данных и визуализация (1 час)
Действия
- Запустить бенчмарк для vLLM:
python benchmark.py --server vllm --url http://localhost:8000/v1/completions --model llama-2-7b --output vllm_results.csv - Остановить vLLM, запустить TGI, повторить команду с соответствующим URL (
http://localhost:8080/generate). - Построить графики в Jupyter Notebook:
- Boxplot latency (общая)
- Throughput (токенов/сек) для каждого сервера
- Зависимость latency от размера батча (если меняли concurrency)
- GPU utilisation по времени
- Вычислить средний throughput и процент ускорения vLLM относительно TGI.
Ожидаемый результат этапа
Ноутбук с графиками и сводной таблицей метрик.
Этап 5: Анализ и оформление отчёта (1–2 часа)
Действия
-
Сформулировать вывод:
- Достигнуто ли целевое ускорение 20–50%?
- Какие конфигурации влияют на разницу (batch size, max_tokens, GPU memory)?
- Сравнить TTFT (time to first token) и throughput при разной нагрузке.
-
Написать отчёт в формате
README.mdс секциями:- Методология (модель, железо, параметры)
- Результаты (таблицы + графики)
- Выводы и рекомендации
-
Сохранить все артефакты в репозиторий Git.
Ожидаемый результат этапа
Готовый отчёт в репозитории с чётким вердиктом.
5. Критерии приемки (Definition of Done)
- vLLM и TGI развёрнуты в Docker-контейнерах на одном GPU.
- Бенчмарк-скрипт воспроизводим и параметризован (модель, url, concurrency, промпты).
- Собраны метрики для минимум 50 запросов на каждый сервер при одинаковых условиях.
- Построены графики latency и throughput.
- Вычислено процентное ускорение vLLM относительно TGI (или наоборот).
- Проведён анализ влияния размера батча (concurrency) на разницу.
- Написана секция «Выводы» с объяснением причин ускорения/замедления.
- Отчёт содержит раздел «Возможные источники погрешностей» (например, разная реализация batching, разница в квантовании, влияние контейнеров).
- Код бенчмарка и отчёт загружены в Git-репозиторий.
6. Ожидаемый результат
Основной артефакт
Репозиторий со следующей структурой:
benchmark_llm/
├── docker_compose/ # опциональные docker-compose файлы
├── benchmark.py # скрипт нагрузочного тестирования
├── prompts.json # тестовые промпты
├── vllm_results.csv
├── tgi_results.csv
├── analysis.ipynb # ноутбук с графиками
├── report.md # итоговый отчёт
└── README.md # инструкция по запуску
Содержание отчёта
- Таблица сравнения: средний throughput (токенов/сек), p50/p95 latency, TTFT.
- Графики: CDF latency, scatter plot latency vs. batch.
- Вывод: подтверждено ли ускорение vLLM на 20–50%? Если нет – объяснение.
Дополнительные результаты
- Разобран механизм continuous batching в vLLM и TGI.
- Понимание того, как параметры
--gpu-memory-utilizationи--max-batch-prefill-tokensвлияют на производительность.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Модель не влезает в VRAM (Out of Memory) | Использовать --quantization awq или bitsandbytes 4-bit; уменьшить --max-model-len до 2048; взять меньшую модель (7B → 1.1B) |
Ошибка CUDA out of memory при параллельном запуске | Запускать серверы строго последовательно, между тестами перезапускать Docker и чистить кэш (docker system prune -a для верности) |
Разные версии API: vLLM использует /v1/completions, TGI – /generate | Унифицировать вызовы в бенчмарк-скрипте через условный URL и формат payload |
Разница в параметрах по умолчанию (например, max_new_tokens vs max_tokens) | Явно указывать max_tokens и temperature=0 (greedy decoding) для воспроизводимости |
| Нестабильные замеры из-за фоновых процессов | Отключить оболочки (GUI), фиксировать utilisation GPU, повторить тест 3 раза |
| Проблемы с аутентификацией Hugging Face | Использовать --env HF_TOKEN при запуске контейнера; скачать модель заранее через huggingface-cli login |
8. Бюджет времени (оценка)
| Этап | Длительность |
|---|---|
| 1. Подготовка и развёртывание vLLM | 2–3 ч |
| 2. Развёртывание TGI | 1.5–2 ч |
| 3. Написание бенчмарк-скрипта | 1.5–2 ч |
| 4. Сбор данных и визуализация | 1–1.5 ч |
| 5. Анализ и оформление отчёта | 1–2 ч |
| Итого | 7–10.5 ч |
Примечание Для первого выполнения рекомендую заложить 1 день + запас на отладку (особенно если железо слабое). Повторный прогон займёт не более 2–3 часов.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 10 | Как работает PagedAttention в vLLM? |
| 42 | Сравнение continuous batching vLLM и TGI |
| 67 | Настройка tensor parallelism для одной GPU |
| 134 | Влияние max-batch-prefill-tokens на throughput |
| 203 | Метрики: throughput, latency, TTFT – определения |
| 210 | Почему vLLM быстрее при одинаковом hardware? |
| 311 | Оптимизация GPU memory utilisation для inference |
| 405 | Асинхронное нагрузочное тестирование с aiohttp |
| 512 | Оценка производительности LLM сервера через Prometheus |
| 689 | Квантование моделей (AWQ, GPTQ) и его влияние на throughput |
10. Чек-лист самопроверки
- Я развернул vLLM и TGI с одинаковыми параметрами (модель, max_tokens, dtype).
- Перед каждым тестом я убедился, что второй сервер остановлен, чтобы не делить GPU.
- Я использую одинаковый набор промптов и одинаковое количество запросов для обоих серверов.
- Я измерил throughput как (сумма всех сгенерированных токенов) / (общее время теста).
- Я проверил, что ответы содержат
usageс количеством токенов, или посчитал их через tokenizer. - Я построил хотя бы один график (CDF latency, throughput vs concurrency).
- Я сформулировал конкретные цифры ускорения/замедления и обосновал их технически.
- Я описал, какие погрешности могли повлиять на результат (температура, фоновые процессы, версии CUDA).
- Я сохранил исходные CSV-файлы и код бенчмарка в Git.