Блог AST-SoftPro
RPC для распределённого инференса: как собрать LLM на нескольких машинах в vLLM, llama.cpp и других движках
Модели растут быстрее, чем видеопамять одной машины. Qwen3-235B, Llama-3.1-405B, DeepSeek-V3 не помещаются даже в топовую H100 с 80 ГБ, не говоря о домашних RTX 4090 и Mac Studio на 192 ГБ unified memory. Выход один — разнести инференс по нескольким узлам. Но у каждого движка свой подход: где-то это «настоящий» RPC-протокол между процессами, где-то — переименованные параметры --tensor-parallel-size, где-то — гибрид NCCL + Ray. Разберём, что внутри, как запустить и где грабли.
Что вообще называют «RPC» в распределённом инференсе
Термин путается, потому что разные стеки используют разные механизмы:
-
Настоящий RPC — отдельный бинарный протокол между инстансами движка (как
rpc-serverв llama.cpp): один узел держит модель и рассылает слои/тензоры на воркеров по TCP. -
Коллективные коммуникации (NCCL/Gloo/UCX) — то, что vLLM и SGLang называют «tensor parallel» через
--tensor-parallel-size N. Формально это тоже удалённые вызовы, но с тёплой связью по RDMA/InfiniBand. -
Ray/multi-process — оркестратор поверх обычных Python-процессов: один «head» координирует воркеров, не привязываясь к конкретному транспорту.
-
Data parallel — несколько независимых копий модели на разных GPU, батчи роутятся между ними (в vLLM это
--data-parallel-size).
Все четыре подхода решают одну задачу — заставить одну логическую модель работать на N устройствах, — но отличаются по задержкам, требованиям к сети и масштабируемости.
llama.cpp: настоящий rpc-server и пайплайн-параллелизм
У llama.cpp свой путь. С момента мержа кода Radoslav Gerganova (см. обсуждение ggml-org/llama.cpp#15796) любой GGUF можно нарезать по слоям на несколько машин, используя встроенный RPC-бэкенд. Это не «тензорный параллелизм» в смысле NCCL — это честный pipeline parallel: каждый узел хранит свой диапазон слоёв и активации между ними передаются по TCP.
Сборка с поддержкой RPC
На каждом узле (включая head):
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build -DGGML_CUDA=ON -DGGML_RPC=ON
cmake --build build --config Release -j
DGGML_RPC=ON включает серверную часть. CUDA-флаг опционален — воркеры могут быть и чисто CPU, и на Apple Silicon (Metal/CPU backend).
Запуск воркеров
На каждом узле, отдающем ресурсы:
./build/bin/rpc-server --host 0.0.0.0 --port 50052 --threads 8
Для продакшена заворачивают в systemd:
# /etc/systemd/system/llama-rpc.service
[Unit]
Description=llama.cpp RPC worker
After=network.target
[Service]
ExecStart=/opt/llama.cpp/build/bin/rpc-server --host 0.0.0.0 --port 50052 --threads 8
Restart=always
User=ai
Group=ai
[Install]
WantedBy=multi-user.target
Подключение с head-узла
./build/bin/llama-server \
-m /models/Meta-Llama-3.1-70B-Instruct.Q4_K_M.gguf \
--rpc 192.168.1.42:50052,192.168.1.43:50052 \
--host 0.0.0.0 --port 8080 \
-ngl 99 -c 4096 -t 8
Что здесь важно:
-
--rpc host:port,...— список воркеров. Их может быть 2, 5, 10 — llm.cpp сам распределит слои. -
-ngl 99— «offload все слои»: llama.cpp раскидывает их между локальным GPU и удалёнными RPC-узлами по доступной памяти. -
-t 8— CPU-нити для матричных умножений, которые не поместились на VRAM.
Грабли llama.cpp RPC
-
Нет continuous batching — параллелизм ограничен одним стримом + очередью. Под нагрузкой HTTP-сервера лучше ставить перед ним балансировщик, который держит N независимых
rpc-server-сессий. -
Высокий TTFT — prefill 4–6 секунд против ~1.5 с на одной 4090. Pipeline прыгает по узлам, каждый hop добавляет RTT.
-
Без auth/encryption — RPC-плагин голый TCP. Запускайте в private VPC или за WireGuard/Tailscale.
-
Квантизация гибкая — Q4_K_M на одном узле, Q5_K_M на другом для тестов;
mixed-quantофициально поддерживается. -
Сеть решает — на 1 GbE ~3 tok/s, на 2.5 GbE ~6 tok/s на одном стриме (замер из Local AI Master). Для приличного TTFT нужна 10 GbE или InfiniBand.
Когда выбирать: у вас CPU/Mac-кластер с большим unified memory (M3 Ultra, Epyc-сервера на 768 ГБ RAM), или нужно быстро подключить домашний ПК как «костыль» к серверу.
vLLM: tensor parallel, pipeline parallel и data parallel
vLLM не использует слово «RPC» в своём API — там это NCCL/Gloo + Ray или multiprocessing. Но логика та же: модель нарезается, слои обмениваются активациями.
Базовые стратегии
| Параметр | Что делает | Когда использовать |
|---|---|---|
--tensor-parallel-size N |
Разрезает веса каждого слоя на N частей по GPU (NCCL all-reduce) | N GPU на одной машине с NVLink/NVSwitch |
--pipeline-parallel-size M |
Разрезает модель по слоям на M стадий | Модель не влезает в один узел, M узлов |
--data-parallel-size K |
K независимых копий модели | Утилизация кластера, высокий RPS |
Внутриузловой TP и межузловой PP — классическая комбинация:
# На каждом из 2 узлов с 8x H100
vllm serve meta-llama/Llama-3.1-405B \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--data-parallel-size 1
tp_size × pp_size × dp_size = суммарное число GPU, которое нужно.
Multi-node запуск через multiprocessing
Чтобы не тащить Ray, vLLM умеет стартовать через обычный multiprocessing engine. Head-нода координирует воркеров по SSH/Gloo:
# На head-узле
vllm serve meta-llama/Llama-3.1-405B-Instruct \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--head-rank 0 \
--node-rank 0 \
--num-nodes 2 \
--master-addr 10.0.0.1 \
--master-port 29500
# На втором узле
vllm serve meta-llama/Llama-3.1-405B-Instruct \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--head-rank 0 \
--node-rank 1 \
--num-nodes 2 \
--master-addr 10.0.0.1 \
--master-port 29500
Для Docker-варианта — см. официальный гайд Distributed Inference and Serving. Там же про настройку NCCL_SOCKET_IFNAME и UCX_NET_DEVICES, без которых NCCL уйдёт не на ту сетевую карту.
Когда что выбирать в vLLM
-
Только TP — все GPU в одной машине, NVLink между ними. Это самый быстрый режим, all-reduce по шине почти бесплатен.
-
TP+PP — крупная модель, несколько DGX/HGX-стоек. TP внутри стойки по NVLink, PP между стойками через IB/RoCE.
-
DP — есть свободные GPU, нужна пропускная способность по запросам, а не по размеру модели. Полезно для чат-ботов с большим RPS.
-
TP+DP — смесь: внутри каждой «реплики» модель порезана по TP, реплик несколько. Это то, что Jarvislabs.ai рекомендует для серьёзного продакшена.
SGLang: TP/DP/EP и более тонкое управление
SGLang часто обгоняет vLLM на data-parallel сценариях — на форумах есть сравнения, где SGLang с --dp 2 выдаёт ~150% больше токенов, чем vLLM с TP. Ключевые параметры:
python -m sglang.launch_server \
--model-path meta-llama/Llama-3.1-70B-Instruct \
--tp-size 4 \
--dp-size 2 \
--ep-size 1
-
--tp-size— аналог vLLM TP, режет веса по слоям. -
--dp-size— несколько независимых копий для утилизации. -
--ep-size— expert parallel для MoE (DeepSeek-V3, Mixtral). Разносит экспертов между GPU.
SGLang использует свой движок распределённых вычислений поверх NCCL, и для multi-node требует корректной настройки сетевых интерфейсов (те же NCCL_SOCKET_IFNAME, что и в vLLM). Документация по multi-node — на docs.sglang.io.
TensorRT-LLM и NVIDIA Dynamo
Если стек полностью NVIDIA — стоит смотреть в сторону TensorRT-LLM. Он умеет тот же TP/PP/CP (context parallel), но с оптимизациями под Hopper/Blackwell и собственным движком компиляции ядер. Поверх него NVIDIA кладёт Dynamo — disaggregation-оркестратор, который разделяет prefill и decode на разные пулы GPU (prefill требует много памяти и compute, decode — много памяти и latency-sensitive).
Dynamo работает с vLLM, SGLang и TRT-LLM как бэкендами; на бытовом железе он избыточен, но в продакшен-кластерах H100/H200 уже стандарт.
Движки попроще: Ollama, LM Studio, ExLlamaV2
Ollama исторически не имела RPC, но через подкапотный llama.cpp теперь поддерживает тот же --rpc-флаг в переменных окружения. Для домашнего использования проще собрать кластер из 2–3 Mac Studio на llama.cpp RPC, чем мучиться с NCCL. LM Studio и ExLlamaV2 — single-machine only; распределёнки нет.
Какой стек выбрать
| Задача | Лучший выбор | Почему |
|---|---|---|
| Домашний кластер из 2–3 Mac/ПК | llama.cpp RPC | Самый низкий порог входа, нет NCCL |
| Один сервер с 1–8 GPU | vLLM | Continuous batching, PagedAttention, production-ready |
| 16+ GPU, высокий RPS | vLLM или SGLang с DP | Data parallel даёт throughput |
| MoE 200B+ | SGLang или TRT-LLM | Expert parallel, оптимизации под MoE |
| NVIDIA-стек, полный пайплайн | TRT-LLM + Dynamo | Disaggregation prefill/decode |
| Гибрид GPU/CPU/Mac | llama.cpp RPC | Единственный нормальный вариант |
Что важно помнить в любом стеке
-
Сеть решает больше, чем кажется. TP требует синхронных all-reduce на каждый слой — 1 GbE убьёт производительность. PP терпимее, но тоже требует предсказуемых задержек.
-
NVLink между GPU в одном узле, IB/RoCE между узлами. Если есть только Ethernet — PP лучше, чем TP.
-
Мониторьте NCCL_DEBUG=INFO при первом запуске: видно, на каких интерфейсах NCCL стартует, и сразу понятно, почему всё медленно.
-
Версии драйверов и CUDA должны совпадать на всех узлах. NCCL очень чувствителен к mixed-versions.
-
Тестируйте TTFT отдельно от генерации. Pipeline даёт терпимую скорость генерации, но prefill проседает.
Распределённый инференс перестал быть экзотикой — это нормальный способ запустить большую модель на коллективном железе. Главное — правильно подобрать стек под топологию и не пытаться прогнать тензорный параллелизм через Wi-Fi роутер.