專家並行部署¶
vLLM 支援專家並行 (EP),它允許將混合專家 (MoE) 模型中的專家部署在獨立的 GPU 上,從而整體提高區域性性、效率和吞吐量。
EP 通常與資料並行 (DP) 結合使用。雖然 DP 可以獨立於 EP 使用,但與 DP 結合使用時 EP 更高效。您可以在此處閱讀更多關於資料並行性的資訊。
先決條件¶
在使用 EP 之前,您需要安裝必要的依賴項。我們正在積極努力使未來的安裝過程更簡單
- 安裝 DeepEP 和 pplx-kernels:按照 vLLM 關於 EP 核心的指南設定主機環境此處。
- 安裝 DeepGEMM 庫:遵循官方說明。
- 對於解耦服務:按照指令碼安裝 UCX 和 NIXL。
後端選擇指南¶
vLLM 為 EP 提供了三種通訊後端
後端 | 用例 | 特性 | 最佳應用場景 |
---|---|---|---|
pplx |
單節點 | 支援分塊預填充 | 開發,最適用於節點內(單節點)部署 |
deepep_high_throughput |
多節點預填充 | 帶連續佈局的 grouped GEMM | 高吞吐量場景,預填充主導型工作負載 |
deepep_low_latency |
多節點解碼 | 支援 CUDA graph,掩碼佈局 | 低延遲場景,解碼主導型工作負載 |
單節點部署¶
警告
EP 是一項實驗性功能。引數名稱和預設值未來可能會發生變化。
配置¶
透過設定 --enable-expert-parallel
標誌啟用 EP。EP 大小將自動計算為
其中:- TP_SIZE
:張量並行大小(目前始終為 1)- DP_SIZE
:資料並行大小- EP_SIZE
:專家並行大小(自動計算)
示例命令¶
以下命令將部署一個 DeepSeek-V3-0324
模型,採用 1 路張量並行、8 路(注意力)資料並行和 8 路專家並行。注意力權重在所有 GPU 上覆制,而專家權重則在 GPU 之間分割。它將在一個配備 8 塊 GPU 的 H200(或 H20)節點上執行。對於 H100,您可以嘗試部署一個較小的模型或參考多節點部署部分。
# Single node EP deployment with pplx backend
VLLM_ALL2ALL_BACKEND=pplx VLLM_USE_DEEP_GEMM=1 \
vllm serve deepseek-ai/DeepSeek-V3-0324 \
--tensor-parallel-size 1 \ # Tensor parallelism across 1 GPU
--data-parallel-size 8 \ # Data parallelism across 8 processes
--enable-expert-parallel # Enable expert parallelism
多節點部署¶
對於多節點部署,請使用 DeepEP 通訊核心,並選擇以下兩種模式之一(參見上方的後端選擇指南)。
部署步驟¶
- 每個節點執行一個命令 - 每個節點都需要自己的啟動命令
- 配置網路 - 確保正確的 IP 地址和埠配置
- 設定節點角色 - 第一個節點處理請求,其他節點以無頭模式執行
示例:2節點部署¶
以下示例展示了使用 deepep_low_latency
模式在 2 個節點上部署 DeepSeek-V3-0324
模型
# Node 1 (Primary - handles incoming requests)
VLLM_ALL2ALL_BACKEND=deepep_low_latency VLLM_USE_DEEP_GEMM=1 \
vllm serve deepseek-ai/DeepSeek-V3-0324 \
--tensor-parallel-size 1 \ # TP size per node
--enable-expert-parallel \ # Enable EP
--data-parallel-size 16 \ # Total DP size across all nodes
--data-parallel-size-local 8 \ # Local DP size on this node (8 GPUs per node)
--data-parallel-address 192.168.1.100 \ # Replace with actual IP of Node 1
--data-parallel-rpc-port 13345 \ # RPC communication port, can be any port as long as reachable by all nodes
--api-server-count=8 # Number of API servers for load handling (scaling this out to total ranks are recommended)
# Node 2 (Secondary - headless mode, no API server)
VLLM_ALL2ALL_BACKEND=deepep_low_latency VLLM_USE_DEEP_GEMM=1 \
vllm serve deepseek-ai/DeepSeek-V3-0324 \
--tensor-parallel-size 1 \ # TP size per node
--enable-expert-parallel \ # Enable EP
--data-parallel-size 16 \ # Total DP size across all nodes
--data-parallel-size-local 8 \ # Local DP size on this node
--data-parallel-start-rank 8 \ # Starting rank offset for this node
--data-parallel-address 192.168.1.100 \ # IP of primary node (Node 1)
--data-parallel-rpc-port 13345 \ # Same RPC port as primary
--headless # No API server, worker only
關鍵配置說明¶
- 無頭模式:輔助節點使用
--headless
標誌執行,這意味著所有客戶端請求都由主節點處理 - Rank 計算:
--data-parallel-start-rank
應等於先前節點的累積本地 DP 大小 - 負載擴容:調整主節點上的
--api-server-count
以處理更高的請求負載
網路配置¶
InfiniBand 叢集
在 InfiniBand 網路叢集上,設定此環境變數以防止初始化掛起
這確保了 torch 分散式組發現使用乙太網而非 InfiniBand 進行初始設定。專家並行負載均衡器 (EPLB)¶
儘管 MoE 模型通常經過訓練,使得每個專家接收相似數量的 token,但實際上 token 在專家之間的分佈可能高度不均勻。vLLM 提供專家並行負載均衡器 (EPLB) 以在 EP 排名之間重新分配專家對映,從而平衡專家之間的負載。
配置¶
使用 --enable-eplb
標誌啟用 EPLB。
模型支援
目前僅支援 DeepSeek V3 架構。
啟用後,vLLM 會在每次前向傳播時收集負載統計資料,並定期重新平衡專家分佈。
EPLB 引數¶
引數 | 描述 | 預設值 |
---|---|---|
--eplb-window-size |
用於再平衡決策跟蹤的引擎步數 | - |
--eplb-step-interval |
再平衡頻率(每 N 個引擎步) | - |
--eplb-log-balancedness |
記錄平衡性指標(每個專家的平均 token 數 ÷ 每個專家的最大 token 數) | false |
--num-redundant-experts |
每個 EP 排名除了等量分配之外的額外全域性專家數量 | 0 |
專家分佈公式¶
- 預設:每個 EP 排名擁有
NUM_TOTAL_EXPERTS ÷ NUM_EP_RANKS
個專家 - 帶冗餘:每個 EP 排名擁有
(NUM_TOTAL_EXPERTS + NUM_REDUNDANT_EXPERTS) ÷ NUM_EP_RANKS
個專家
示例命令¶
啟用 EPLB 的單節點部署
# Single node with EPLB load balancing
VLLM_ALL2ALL_BACKEND=pplx VLLM_USE_DEEP_GEMM=1 vllm serve deepseek-ai/DeepSeek-V3-0324 \
--tensor-parallel-size 1 \ # Tensor parallelism
--data-parallel-size 8 \ # Data parallelism
--enable-expert-parallel \ # Enable EP
--enable-eplb \ # Enable load balancer
--eplb-log-balancedness \ # Log balancing metrics
--eplb-window-size 1000 \ # Track last 1000 engine steps
--eplb-step-interval 3000 # Rebalance every 3000 steps
對於多節點部署,將這些 EPLB 標誌新增到每個節點的命令中。我們建議在大規模用例中將 --num-redundant-experts
設定為 32,以便最受歡迎的專家始終可用。
解耦服務(預填充/解碼分離)¶
對於需要嚴格 SLA 保證首個 token 時間和 token 間延遲的生產部署,解耦服務允許預填充和解碼操作獨立擴充套件。
架構概述¶
- 預填充例項:使用
deepep_high_throughput
後端以獲得最佳預填充效能 - 解碼例項:使用
deepep_low_latency
後端以獲得最小解碼延遲 - KV 快取傳輸:透過 NIXL 或其他 KV 聯結器連線例項
設定步驟¶
-
安裝 KV 聯結器:使用安裝指令碼安裝 NIXL
-
配置兩個例項:將此標誌新增到預填充和解碼例項:
--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both"}
-
客戶端編排:使用下面的客戶端指令碼協調預填充/解碼操作。我們正在積極開發路由解決方案。
客戶端編排示例¶
from openai import OpenAI
import uuid
try:
# 1: Set up clients for prefill and decode instances
openai_api_key = "EMPTY" # vLLM doesn't require a real API key
# Replace these IP addresses with your actual instance addresses
prefill_client = OpenAI(
api_key=openai_api_key,
base_url="http://192.168.1.100:8000/v1", # Prefill instance URL
)
decode_client = OpenAI(
api_key=openai_api_key,
base_url="http://192.168.1.101:8001/v1", # Decode instance URL
)
# Get model name from prefill instance
models = prefill_client.models.list()
model = models.data[0].id
print(f"Using model: {model}")
# 2: Prefill Phase
# Generate unique request ID to link prefill and decode operations
request_id = str(uuid.uuid4())
print(f"Request ID: {request_id}")
prefill_response = prefill_client.completions.create(
model=model,
# Prompt must exceed vLLM's block size (16 tokens) for PD to work
prompt="Write a detailed explanation of Paged Attention for Transformers works including the management of KV cache for multi-turn conversations",
max_tokens=1, # Force prefill-only operation
extra_body={
"kv_transfer_params": {
"do_remote_decode": True, # Enable remote decode
"do_remote_prefill": False, # This is the prefill instance
"remote_engine_id": None, # Will be populated by vLLM
"remote_block_ids": None, # Will be populated by vLLM
"remote_host": None, # Will be populated by vLLM
"remote_port": None # Will be populated by vLLM
}
},
extra_headers={"X-Request-Id": request_id}
)
print("-" * 50)
print("✓ Prefill completed successfully")
print(f"Prefill response: {prefill_response.choices[0].text}")
# 3: Decode Phase
# Transfer KV cache parameters from prefill to decode instance
decode_response = decode_client.completions.create(
model=model,
prompt="This prompt is ignored during decode", # Original prompt not needed
max_tokens=150, # Generate up to 150 tokens
extra_body={
"kv_transfer_params": prefill_response.kv_transfer_params # Pass KV cache info
},
extra_headers={"X-Request-Id": request_id} # Same request ID
)
print("-" * 50)
print("✓ Decode completed successfully")
print(f"Final response: {decode_response.choices[0].text}")
except Exception as e:
print(f"❌ Error during disaggregated serving: {e}")
print("Check that both prefill and decode instances are running and accessible")