English translation is not available yet. Showing Russian content.

Как масштабировать vLLM на несколько GPU/нод?

Краткий тезис

Масштабирование vLLM на несколько GPU и нод решает задачу размещения больших моделей (например, Llama 70B) и увеличения пропускной способности (throughput). Основные стратегии: Tensor Parallelism (TP) (разрезание слоёв внутри одной ноды), Pipeline Parallelism (PP) (разбиение модели на стадии между нодами) и Data Parallelism (DP) (реплики модели за балансировщиком). Ключевые вызовы — накладные расходы на межнодовую коммуникацию через NCCL, отсутствие разделения KV cache между репликами и сложность балансировки нагрузки при гетерогенном оборудовании.

1. Введение: Зачем масштабировать vLLM

vLLM — популярный inference-сервер для LLM, оптимизированный за счёт PagedAttention и эффективного управления KV cache. Однако одна GPU (даже с 80 ГБ) не может вместить 70B+ модель в FP16 (140+ ГБ). Даже если модель помещается, одна GPU обрабатывает запросы последовательно, что ограничивает throughput (количество токенов/сек). Масштабирование на несколько GPU и нод позволяет:

  • Разместить модель, не влезающую в одну GPU (TP/PP).
  • Увеличить throughput за счёт параллельной обработки (DP).
  • Снизить latency за счёт распределения вычислений (TP снижает time-to-first-token, DP может увеличить его из-за очередей — нужен балансировщик).

2. Tensor Parallelism (TP) — параллелизм тензоров

Определение Разрезание слоёв (весов) модели вдоль скрытой размерности (hidden dimension) или линейных проекций. Каждая GPU выполняет часть вычислений одного и того же слоя, после чего синхронизируется. Требуется высокая скорость межсоединения (NVLink, NVSwitch) — поэтому применяется в пределах одной ноды.

  • В vLLM включается флагом --tensor-parallel-size, например --tensor-parallel-size 8 для 8 GPU.
  • Как это работает Матричные умножения (nn.Linear) делятся на несколько GPU. Для Attention heads — равномерное распределение голов по GPU. Для FFN — разрезание по оси features.
  • Коммуникация all-reduce после каждого слоя — значительный overhead, поэтому TP наиболее эффективен при быстром межсоединении.
  • Преимущества низкая latency на первый токен (все GPU обрабатывают один step одновременно), линейное ускорение до порога насыщения.
  • Ограничения масштабируется только до 8–16 GPU из-за накладных расходов на синхронизацию.

3. Pipeline Parallelism (PP) — конвейерный параллелизм

Определение Модель разбивается на последовательные стадии (например, первые 16 слоев на ноде 1, следующие 16 на ноде 2). Каждая стадия занимает отдельную ноду или группу GPU. Запросы передаются по конвейеру.

  • В vLLM комбинируется с TP: внутри ноды используется TP, между нодами — PP. vLLM не имеет встроенного PP (он ориентирован на TP), поэтому PP приходится реализовывать на уровне кластера или через внешние фреймворки (например, SGLang, Triton Inference Server). Однако vLLM поддерживает multi-node режим через --distributed-executor-backend=ray и настройки pipeline parallelism через Ray.
  • Как это работает (с Ray): Каждая группа GPU (нода) выполняет часть модели. Данные передаются через NCCL между нодами, но уже не на каждом слое, а на границах стадий — overhead ниже, чем у TP между нодами.
  • Преимущества позволяет масштабировать модель на слабо связанные ноды (Ethernet вместо InfiniBand), так как межнодовый трафик ниже.
  • Недостатки возникает "баббл" (простои GPU) из-за неравномерной загрузки; latency на первый токен выше (нужно ждать заполнения конвейера).

4. Data Parallelism (DP) — параллелизм данных

Определение На каждом GPU (или группе GPU с TP/PP) развёрнута полная копия модели (реплика). Запросы равномерно распределяются между репликами через балансировщик (например, Nginx , Kubernetes Service, AWS ALB).

  • В vLLM это самый простой способ масштабирования: запустить несколько независимых инстансов vLLM на разных портах/подах, поставить перед ними round-robin балансировщик.
  • Преимущества линейный рост throughput, устойчивость к отказам, не требует синхронизации между репликами (KV cache не разделяется).
  • Недостатки каждая реплика потребляет полный объём GPU RAM, нет ускорения одного запроса (первая токен latency не улучшается), требуется балансировка с учётом занятости (например, least connections).

5. Комбинированная стратегия: TP + PP + DP

На крупных кластерах (16–64+ GPU) используется иерархия:

  • DP на верхнем уровне: несколько реплик модели.
  • TP внутри каждой реплики: 4–8 GPU с быстрым межсоединением.
  • PP между группами TP разных нод (если модель не влезает в 8 GPU из-за памяти).

Пример: модель 70B, нужно 140 ГБ памяти (FP16). Одна A100 (80 ГБ) не вмещает. TP=2 (2 GPU, 160 ГБ) — вмещается. Для throughput запускаем DP=4 (8 GPU всего) с балансировщиком.

В многонодовом сценарии: 2 ноды по 4 GPU. На каждой ноде TP=4 (все 4 GPU ноды — для низкой latency), между нодами PP (первая половина модели на первой ноде, вторая на второй). Или две полные реплики (DP=2), каждая с TP=4 (всего 8 GPU). Выбор зависит от приоритета: latency (TP+PP) vs throughput (DP).

6. Проблемы и ограничения

  • NCCL communication overhead между нодами при TP ноды обмениваются данными на каждом шаге. Если нет InfiniBand (IB), пропускная способность Ethernet (25–100 GbE) будет узким местом. Для PP трафик реже, но всё равно критичен.
  • KV cache не шарится между репликами: при DP каждая реплика хранит свой кэш, что ведёт к избыточному потреблению памяти при одинаковых контекстах (например, при prefix caching). vLLM поддерживает automatic prefix caching (APC) , но он работает только в рамках одного инстанса.
  • Балансировка нагрузки round-robin не учитывает текущую загрузку GPU (KV cache occupation, количество активных запросов). Лучше использовать load balancer с поддержкой active connections или микросервис состояния (например, Ray Serve).
  • Гетерогенные ноды разные типы GPU (A100 vs H100) требуют разного tensor-parallel-size, что усложняет распределение.

7. Практическая настройка vLLM

Одна нода, 8 GPU (TP=8)

python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-2-70b-chat-hf \
  --tensor-parallel-size 8 \
  --host 0.0.0.0 --port 8000

Две ноды с TP (каждая нода 4 GPU) + PP через Ray

Установить Ray, запустить head node, подключить worker nodes:

# На head node
ray start --head --port=6379

# На worker node
ray start --address=<head_ip>:6379

# Запуск vLLM с multi-node
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-2-70b-chat-hf \
  --tensor-parallel-size 4 \
  --pipeline-parallel-size 2 \
  --distributed-executor-backend ray \
  --host 0.0.0.0 --port 8000

Примечание --pipeline-parallel-size в vLLM — экспериментальный флаг (на момент v0.6). На практике чаще используют внешний PP через SGLang или Triton.

Data Parallelism (DP) — несколько инстансов

# Инстанс 1 (GPU 0-3)
CUDA_VISIBLE_DEVICES=0,1,2,3 vllm ... --port 8001

# Инстанс 2 (GPU 4-7)
CUDA_VISIBLE_DEVICES=4,5,6,7 vllm ... --port 8002

# Балансировщик (Nginx upstream)

Для DP можно также использовать vLLM с --num-scheduler-steps и Kubernetes HPA.

8. Сравнение стратегий

ПараметрTensor Parallelism (TP)Pipeline Parallelism (PP)Data Parallelism (DP)
МасштабируемостьДо 8-16 GPU (внутри ноды)Много нод (сотни)Горизонтально (реплики)
Latency (TTFT)Низкая (параллельный шаг)Высокая (баббл)Не улучшается (та же модель)
ThroughputРастёт до насыщения коммуникацииУмеренный (из-за простоев)Линейно с числом реплик
Требования к сетиNVLink/NVSwitch (высокая)InfiniBand или быстрый EthernetНе критично (только запросы)
Сложность настройкиПростая (флаг)Средняя (Ray, координация)Простая (балансировщик)
Использование памятиЭффективное (суммарно)ЭффективноеИзбыточное (копии)

9. Пет-проект для закрепления

Задача Развернуть vLLM на двух нодах (можно эмулировать две VM или docker-контейнеры с разными портами), используя комбинацию TP внутри ноды и DP (две реплики). Измерить throughput при одном и двух инстансах.

Инструменты

  • Python, vLLM, Docker (для изоляции), Locust или wrk для генерации нагрузки.
  • Две виртуальные машины с 4 GPU каждая (например, g4dn.12xlarge на AWS с 4 T4).

Шаги:

  1. На каждой ноде поднять инстанс vLLM с --tensor-parallel-size 4, разные порты (8001, 8002).
  2. Поставить перед ними балансировщик (Nginx с upstream).
  3. С помощью Locust/wrk отправить 100 последовательных запросов к одному инстансу, затем к балансировщику.
  4. Замерить среднее время ответа (latency) и количество токенов в секунду (throughput с помощью --output-fields).
  5. Сравнить результат с теоретическим: DP должен удвоить throughput при условии отсутствия узкого места на балансировщике.

Ожидаемый результат Балансировщик показывает ~2x throughput по сравнению с одиночным инстансом при идентичном latency (или немного выше из-за накладных расходов сети). При загрузке >70% может проявиться асимметрия.

10. Связь с другими вопросами

ВопросТема
810Основы vLLM: архитектура, PagedAttention
815Оптимизация памяти KV cache в vLLM (prefix caching)
825Балансировка нагрузки для LLM inference
830InfiniBand vs Ethernet: влияние на производительность распределённого инференса
500Распределённое обучение моделей (Data Parallelism, Pipeline Parallelism)
615Inference quantization (FP8, INT4) – как сократить потребление памяти и уменьшить TP

11. Навигация


Навигация