Когда модель не помещается на одну GPU
Современные LLM с десятками и сотнями миллиардов параметров требуют десятков гигабайт памяти только для весов. Если модель не помещается в VRAM одной видеокарты, есть два основных пути: квантование (уменьшение точности весов) или распределение модели по нескольким GPU. Tensor parallelism — второй путь, причём он сохраняет исходную точность и не вносит потерь качества.
Идея проста: каждая матрица весов внутри трансформерного слоя разрезается на части, и каждая GPU хранит и обрабатывает свой фрагмент. После вычисления фрагментов результаты синхронизируются через коллективные операции (обычно AllReduce или AllGather). Это позволяет обрабатывать один запрос одновременно на нескольких устройствах.
Механика: что именно разрезается
В трансформерном блоке основные матричные умножения происходят в:
- Attention-слоях — проекции Q, K, V и выходная проекция.
- FFN/MLP-слоях — две линейные проекции (up и down).
При tensor parallelism каждая из этих матриц разбивается по столбцам или строкам между GPU. Например, при
tensor_parallel_size=4 матрица весов размером [hidden, 4*hidden] делится на четыре блока [hidden, hidden], каждый из которых размещается на отдельной GPU. После локального умножения выполняется AllReduce для суммирования частичных результатов.Это означает, что на каждом шаге генерации (каждом токене) происходит обмен данными между GPU. Поэтому пропускная способность и задержка межкарточного соединения напрямую влияют на скорость инференса.
Требования к оборудованию и топологии
| Фактор | Влияние на tensor parallelism |
|---|---|
| NVLink между GPU | Низкая задержка, высокая пропускная способность — оптимально |
| PCIe Gen4/Gen5 | Работает, но задержка выше; при большом tp_size деградация заметна |
| Отсутствие NVLink (например, L40S) | Лучше использовать pipeline parallelism вместо tensor parallelism |
| InfiniBand между узлами | Необходимо для эффективного меж узлового tensor parallelism |
| Обычный Ethernet (TCP) | NCCL будет использовать сокеты — крайне неэффективно для TP между узлами |
Практическое правило:
tensor_parallel_size устанавливают равным числу GPU в одном узле. Если модель не помещается на один узел, добавляют pipeline_parallel_size — число узлов.Настройка в vLLM: один узел, несколько GPU
vLLM реализует tensor parallelism на основе алгоритма из Megatron-LM. Для запуска на четырёх GPU одного сервера достаточно одного параметра:
Python:
from vllm import LLM
llm = LLM("facebook/opt-13b", tensor_parallel_size=4)
output = llm.generate("San Francisco is a")
Для серверного режима:
Bash:
vllm serve facebook/opt-13b \
--tensor-parallel-size 4
По умолчанию на одном узле vLLM использует нативный Python multiprocessing как рантайм. Это не требует установки дополнительных зависимостей.
Комбинация с pipeline parallelism
Если число GPU не делит размер модели равномерно (например, 70B-модель на 3 GPU), tensor parallelism может не сработать. В этом случае включают pipeline parallelism, который разрезает модель по слоям и поддерживает неравномерное разбиение:
Bash:
vllm serve meta-llama/Llama-2-70b-hf \
--tensor-parallel-size 1 \
--pipeline-parallel-size 3
Для двух узлов по 8 GPU каждый:
Bash:
vllm serve /path/to/model \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--distributed-executor-backend ray
Здесь
tensor_parallel_size=8 — число GPU внутри узла, pipeline_parallel_size=2 — число узлов. Меж узловой обмен идёт через Ray или multiprocessing.Multi-node: Ray и multiprocessing
Для нескольких узлов vLLM поддерживает два рантайма:
- Ray — распределённый фреймворк, обеспечивает fault tolerance и масштабирование. Требует явной установки (
pip install ray).
- Multiprocessing — встроенный механизм, не требует дополнительных пакетов.
Пример с multiprocessing на двух узлах:
Bash:
## Head node (rank 0)
vllm serve /path/to/model \
--tensor-parallel-size 8 --pipeline-parallel-size 2 \
--nnodes 2 --node-rank 0 \
--master-addr <HEAD_NODE_IP>
## Worker node (rank 1)
vllm serve /path/to/model \
--tensor-parallel-size 8 --pipeline-parallel-size 2 \
--nnodes 2 --node-rank 1 \
--master-addr <HEAD_NODE_IP> --headless
Оба узла должны иметь идентичное окружение: одинаковые пути к модели, одинаковые версии пакетов. Контейнерные образы — самый надёжный способ обеспечить это.
Проверка результата: KV cache и concurrency
После запуска vLLM выводит диагностические строки:
Код:
INFO 07-23 13:56:04 [kv_cache_utils.py:775] GPU KV cache size: 643,232 tokens
INFO 07-23 13:56:04 [kv_cache_utils.py:779] Maximum concurrency for 40,960 tokens per request: 15.70x
Первая строка показывает общий объём KV cache в токенах, доступный на всех GPU. Вторая — сколько одновременных запросов можно обслужить при максимальной длине контекста модели. Если значения ниже требуемых, добавляют GPU или узлы.
Оптимизация меж узловой сети: InfiniBand и GPUDirect RDMA
Tensor parallelism генерирует интенсивный обмен данными на каждом токене. Для меж узлового TP критична пропускная способность сети.
InfiniBand — предпочтительный вариант. При использовании контейнеров добавляют флаги
--privileged -e NCCL_IB_HCA=mlx5 (имя устройства уточняют у администратора).GPUDirect RDMA позволяет сетевому адаптеру обращаться к памяти GPU напрямую, минуя CPU и системную память. Для включения в Docker:
Bash:
docker run --gpus all \
--ipc=host \
--shm-size=16G \
-v /dev/shm:/dev/shm \
vllm/vllm-openai
В Kubernetes добавляют capability
IPC_LOCK и монтируют /dev/shm как emptyDir с medium: Memory.Диагностика: как убедиться, что RDMA работает
Запускают vLLM с подробным логированием NCCL:
Bash:
NCCL_DEBUG=TRACE vllm serve ...
В логах ищут строку
[send] via NET/IB/GDRDMA — это означает, что используется InfiniBand с GPUDirect RDMA. Если вместо этого [send] via NET/Socket — трафик идёт через TCP, что неприемлемо для меж узлового tensor parallelism.MoE-модели: отдельная стратегия для экспертов
Для Mixture-of-Experts моделей (Mixtral, DeepSeek-V3, Qwen-MoE) vLLM поддерживает комбинирование: attention-слои распределяются через Data Parallel, а экспертные слои — через Expert Parallel или Tensor Parallel. Это позволяет эффективнее использовать память, поскольку не все эксперты активны для каждого токена.
Типичные ошибки и ограничения
tensor_parallel_sizeне делит число attention heads. Если у модели 32 головы, аtp_size=3, запуск завершится ошибкой. В таком случае используют pipeline parallelism.
- Смешивание разных GPU в одном узле. vLLM ожидает однородные устройства. Разные модели GPU в одном
tensor_parallel_sizeне поддерживаются.
- Забытая переменная
VLLM_HOST_IP. При multi-node каждый воркер должен иметь уникальныйVLLM_HOST_IP, указывающий на его собственный адрес в приватной сети.
- Открытая сеть. Трафик между узлами vLLM не шифруется. Формат данных позволяет выполнить произвольный код при перехвате. Развёртывание допустимо только в изолированной приватной сети.
- Модель не скачана заранее. При использовании Hugging Face Hub модель загружается на каждом узле. Если путь или токен недоступны на одном из узлов, запуск падает. Рекомендуется предзагрузить модель на общий путь или распределённую файловую систему.
Выбор стратегии: краткая схема
| Ситуация | Стратегия |
|---|---|
| Модель помещается на 1 GPU | Без распределения |
| Модель не помещается на 1 GPU, но входит в узел | tensor_parallel_size = N (число GPU в узле) |
| Число GPU не делит модель равномерно | pipeline_parallel_size = N, tensor_parallel_size = 1 |
| GPU без NVLink (L40S и аналоги) | Pipeline parallelism предпочтительнее |
| Модель не входит в один узел | tensor_parallel_size = GPU на узел, pipeline_parallel_size = число узлов |
| MoE-модель | Data Parallel для attention + Expert/Tensor Parallel для экспертов |
Эта схема покрывает большинство практических сценариев инференса от 7B до 400B+ параметров без потери точности.
