Expert Parallelism:MoE 模型的并行切分
谁该读这一篇? 在跑 DeepSeek-V3 / Mixtral / Qwen3-MoE 这类 MoE 模型,要榨干 GPU 算力的工程师。 前置阅读:
01-tensor-parallelism.md。 耗时: 40 分钟 学完能: 1. 讲清 MoE 推理的瓶颈在哪里(AllToAll 通信); 2. 区分 EP / TP / DP 在 MoE 模型上的切法; 3. 解释专家不均衡(routing imbalance)为什么是工程问题; 4. 列出 SGLang 支持的几种 MoE 后端(DeepEP、FlashInfer TRT-LLM MoE 等); 5. 配 DeepSeek-V3 在 8 张 H100 上的最佳并行策略。
1. MoE 推理是什么
Transformer 层结构:
普通 dense: X → Attention → MLP → 输出
MoE: X → Attention → Router → 选 top-k 个 experts → 加权求和 → 输出
- 模型总参数大(DeepSeek-V3 671B)。
- 但每 token 只激活少量参数(DeepSeek-V3 激活 37B)。
- 训练和推理都要解决"experts 怎么分布、怎么通信"的问题。
2. 三种切法
2.1 TP:把每个 expert 内部切片(同 dense)
GPU 0: 所有 experts 的 1/4 column slice
GPU 1: 所有 experts 的 1/4 column slice
...
每 token 经过 router 后激活 top-2 experts;TP 内部的 column → row → AllReduce 和 dense 一样。
代价:每卡装所有 experts 的部分权重,显存利用不充分。
2.2 EP:把不同 experts 分到不同卡
GPU 0: experts [0, 1, 2, 3, ...]
GPU 1: experts [16, 17, 18, ...]
...
每卡只装一部分 experts,显存大幅省下。 代价:token 路由后要跨卡发送(AllToAll)。
2.3 DP:副本
和 dense 一样,多副本,每副本完整一套。最简单但显存最贵。
实战通常是 EP + TP 混合。
3. AllToAll:MoE 推理的核心通信
Token 1 (GPU 0) ─── router 选了 expert 17 (在 GPU 1) ─── 发到 GPU 1
Token 2 (GPU 0) ─── router 选了 expert 3 (在 GPU 0) ─── 留在 GPU 0
Token 3 (GPU 1) ─── router 选了 expert 5 (在 GPU 0) ─── 发到 GPU 0
...
│
└── 每 token 跨卡传 hidden state,experts 算完再返回
每层 MoE 有 2 次 AllToAll:
- dispatch:把 token hidden 发到对应 expert 的卡。
- combine:experts 算完输出,把结果发回 token 原卡。
AllToAll 在 NVLink 带宽下(每对卡 600 GB/s)通常不是瓶颈; 跨节点时(IB 400 Gb/s)变成主要瓶颈。
4. SGLang 的 MoE 后端
源码:srt/layers/moe/。
主要后端:
| 后端 | 文件 | 强在 |
|---|---|---|
| DeepEP | deepep_waterfill.py |
DeepSeek 团队开源,AllToAll 优化极致 |
| Fused MoE Triton | fused_moe_triton/ |
通用,Triton 实现 |
| Cutlass MoE | cutlass_moe.py |
Cutlass 算子 |
| FlashInfer TRT-LLM MoE | flashinfer_trtllm_moe.py |
FlashInfer 集成 |
| EP MoE | ep_moe/ |
Pure EP 实现 |
DeepEP 是 DeepSeek-V3 / Qwen3-MoE 的最佳搭档;其它后端在不同硬件 / 模型上有用。
切换:
--moe-backend deepep
5. 启动 DeepSeek-V3
典型 8 卡 H100 (1 节点):
python -m sglang.launch_server \
--model-path deepseek-ai/DeepSeek-V3 \
--tp 8 \
--enable-ep-moe \
--moe-backend deepep \
--quantization fp8 \
--max-running-requests 256
或者 16 卡跨 2 节点:
# 节点 1, rank 0-7
python -m sglang.launch_server \
--model-path deepseek-ai/DeepSeek-V3 \
--tp 16 \
--enable-ep-moe \
--dist-init-addr <head_node_ip>:5000 \
--nnodes 2 --node-rank 0
# 节点 2, rank 8-15
python -m sglang.launch_server \
--model-path deepseek-ai/DeepSeek-V3 \
--tp 16 \
--enable-ep-moe \
--dist-init-addr <head_node_ip>:5000 \
--nnodes 2 --node-rank 1
跨节点用 NCCL TCP 或 RDMA。
6. 专家不均衡(Expert Imbalance)
router 选的 top-k experts 不是均匀分布的:某些 expert 被频繁选中,造成"热点 expert"所在的 GPU 排队。
业务影响:
- TPOT 抖动。
- 单卡瓶颈。
工程对策:
6.1 EPLB(Expert Parallel Load Balancing)
源码:srt/eplb/。
思路:
- 监测 expert 使用频率。
- 动态把热门 expert 复制到多卡(增加冗余备份)。
- 冷 expert 合并到一卡。
启用:
--enable-eplb \
--eplb-rebalance-num-iterations 1000
每 N 步重新评估 expert 分布。
6.2 静态副本
最热的几个 expert 提前部署成 N 副本:
--ep-num-redundant-experts 2
每个 expert 最多 2 副本,router 选时按负载分流。
7. AllToAll 优化
DeepEP 的几个核心 trick:
- NVLink + RDMA 异构通信:节点内 NVLink,节点间 RDMA,编排成统一原语。
- Token sorting:对要发到同一目标 GPU 的 token 排序,减少零散 send。
- Fine-grained pipeline:dispatch 和 expert 计算重叠。
效果:DeepEP AllToAll 比 NCCL ncclAllToAll 快 1.5-2×。
8. EP 和 TP 的混合
DeepSeek-V3 推荐 EP=8 + TP=1 的"宽 EP":
8 GPU, EP=8:
GPU 0: experts [0..31]
GPU 1: experts [32..63]
...
GPU 7: experts [224..255]
非 MoE 部分(attention 等): TP=8 切分
attention 用 TP,MoE 用 EP,路由分两套通信。
eplb/ 系列文件实现这个混合。
9. 监控指标
sglang:moe_expert_load{expert_id="x", ...} # 每 expert 被选次数
sglang:moe_alltoall_latency_ms # AllToAll 时延
sglang:moe_imbalance_ratio # 最忙 expert / 平均
健康范围:
- imbalance_ratio < 2.0
- alltoall_latency < 30% 单层耗时
10. 排障
| 现象 | 排查 |
|---|---|
| MoE 启动 OOM | EP 太低(单卡装太多 expert);增 EP |
| TPOT 突增 | 某 expert 热点;查 expert_load |
| AllToAll 慢 | NCCL / RDMA 配置;NVLink 是否正确启用 |
| router routing 不准 | 检查 fp8 量化是否影响 router head |
11. 小结
- MoE 推理瓶颈在 AllToAll 通信 + expert 不均衡。
- 三种并行:TP(切 expert 内部)、EP(按 expert 分布)、DP(副本)。
- SGLang 支持 DeepEP / Fused Triton / Cutlass / FlashInfer TRT-LLM 多种后端。
- DeepSeek-V3 推荐 EP=8 + DeepEP 后端 + EPLB 均衡。
- 监控 imbalance_ratio 和 alltoall_latency。
12. 自检
- EP 和 TP 在 MoE 上的取舍?为什么 DeepSeek-V3 推荐 EP?
答案
**TP**:每卡装所有 experts 的列切片。优势:与 dense 部分统一,AllReduce 通信简单。劣势:每卡都装全部 expert,**显存利用差**(每卡都背 256 expert × 1/4 大小)。 **EP**:每卡装一部分 experts 完整。优势:显存按 expert 数严格均分。劣势:token routing 跨卡 → AllToAll 通信。 DeepSeek-V3 推 EP 因为:(a) **expert 多(256)**,TP 时每卡显存被 sparse 权重塞满;(b) **激活稀疏(top-2)**,AllToAll 只传 2/256 的 token;(c) **DeepEP** 把 AllToAll 优化到接近 NVLink 峰值;(d) **EPLB** 解决负载均衡。 实战 EP=8 + DeepEP 比 TP=8 吞吐高 30-50%。- AllToAll 在一层 MoE 里发生几次?分别在哪?
答案
**2 次**: (1) **Dispatch**:每 token 经 router 选 top-k experts,把 token hidden state 发到对应 expert 所在的卡。 (2) **Combine**:experts 算完输出后,把结果发回 token 原卡,按 router weight 加权求和。 实现:每 token 产生 k 个目标位置(top-k),dispatch 把每 token 的 hidden 复制 k 份发出去;combine 把 k 个输出合回原位置。 DeepEP 把这两步 fuse 进通信 kernel,与 expert 计算 overlap,实测占总时延 ~25%(不 fuse 时 ~50%)。- Expert 不均衡的工程对策有哪些?
答案
(1) **EPLB**([`srt/eplb/`](../sglang/python/sglang/srt/eplb/)):周期性统计 expert 使用频次,动态重排——热门 expert **复制**到多卡(增加冗余备份让 router 选时分流),冷门 expert 合并到一卡。 (2) **静态冗余**:`--ep-num-redundant-experts 2` 给最热的几个 expert 提前部署成 2 副本。 (3) **Router 损失项**:训练时加 load-balance loss 让 router 倾向均匀(这是训练侧的事,推理时已固定)。 (4) **批内打散**:调度时把同 expert 高频请求分散到不同 batch,避免单次 forward 内 expert 极度倾斜。 监控 `moe_imbalance_ratio`(最忙 / 平均),健康 < 2.0,> 3.0 必须干预。- DeepEP 比 NCCL AllToAll 快的核心 trick?
答案
(1) **异构通信原语**:节点内用 NVLink(双向 600 GB/s),节点间用 RDMA(IB 400 Gb/s),编排成统一 dispatch + combine API,避免 NCCL 走通用路径的开销。 (2) **Token sorting**:dispatch 前对要发到同一目标 GPU 的 token 排序合并,把零散小包打成大包,减少 send 次数。 (3) **Fine-grained pipeline**:dispatch 的 IO 与 expert 计算 overlap——上一 batch 在算时下一 batch 已在搬。 (4) **避免 padding**:NCCL ncclAllToAll 假设每对 send/recv 同 size,MoE 实际每对 size 不等需要 padding;DeepEP 用 ragged 通信省 padding 字节。 实测 DeepEP 比 ncclAllToAll 快 1.5-2×,与 expert 计算重叠后 MoE 层时延降 40%。- 跨节点跑 DeepSeek-V3 用 NCCL TCP 还是 RDMA?为什么?
答案
**必须 RDMA**(IB 或 RoCE)。 原因:DeepSeek-V3 跨节点 AllToAll 流量大(每 token 几 KB × top-2 × layer 数 × batch),TCP over Ethernet(10/25 Gbps)撑不住,TPOT 直接秒级。 IB 400 Gb/s 让跨节点 AllToAll 跟节点内 NVLink 在一个量级(仍慢 2-3×,但可接受)。 配置:NCCL 自动识别 IB;`NCCL_IB_HCA` / `NCCL_SOCKET_IFNAME` 指定网卡;启动看日志 "NET/IB: ..." 确认走 IB 而非 socket。 没有 IB 的环境:DeepSeek-V3 严格说跑不出生产性能;强行跑就用单节点 + EP=8 + 接受装得下的 prompt 长度上限。13. 下一步
04-disaggregated.md— P/D 分离。02-core-concepts/01-radix-attention.md— KV cache 与 EP 的协同。- 源码:
srt/layers/moe/、srt/eplb/。