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、400 Gb/s 等数值只是特定硬件链路的量级示例,不能当作 SGLang 的性能保证。
4. SGLang 的 MoE 后端
源码:srt/layers/moe/。
主要后端:
| 后端 | 文件 | 强在 |
|---|---|---|
| DeepEP / Waterfill | token_dispatcher/deepep.py、waterfill.py |
AllToAll 与 shared-expert 负载平衡 |
| 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 的最佳搭档;其它后端在不同硬件 / 模型上有用。
切换:
--ep-size 8 --moe-a2a-backend deepep
5. 启动 DeepSeek-V3
典型 8 卡 H100 (1 节点):
sglang serve deepseek-ai/DeepSeek-V3 \
--tp 8 \
--ep-size 8 \
--moe-a2a-backend deepep \
--quantization fp8 \
--max-running-requests 256
或者 16 卡跨 2 节点:
# 节点 1, rank 0-7
sglang serve deepseek-ai/DeepSeek-V3 \
--tp 16 \
--ep-size 16 \
--moe-a2a-backend deepep \
--dist-init-addr <head_node_ip>:5000 \
--nnodes 2 --node-rank 0
# 节点 2, rank 8-15
sglang serve deepseek-ai/DeepSeek-V3 \
--tp 16 \
--ep-size 16 \
--moe-a2a-backend deepep \
--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 计算重叠。
在匹配的硬件和 workload 上,DeepEP AllToAll 可比 NCCL ncclAllToAll 更快;具体倍数
取决于 token 数、消息大小、拓扑和 overlap,不能脱离基准测试引用固定 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:eplb_balancedness{...} # EPLB 均衡度
sglang:forward_execution_seconds_total{...} # forward 执行累计时间
sglang:estimated_flops_per_gpu_total{...} # 每 GPU 估算 FLOPs
健康范围:
- 以
sglang:eplb_balancedness的历史基线为准,持续下降才需要干预; - 将通信时间与
sglang:forward_execution_seconds_total的变化一起比较。
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 均衡。
- 监控
sglang:eplb_balancedness、forward 执行时间和通信 profiler;当前没有通用的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** 解决负载均衡。 某些 DeepSeek-V3 workload 中 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;通信占比应以目标模型和 batch 的 profiling 结果为准。- 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 极度倾斜。 监控 `sglang:eplb_balancedness` 与 expert dispatch 的日志/指标;具体阈值按 模型和 expert 数量建立基线,不要依赖不存在的 `moe_imbalance_ratio`。- 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` 更快;与 expert 计算重叠后的收益同样依赖消息分布和硬件,需用目标 workload 实测。- 跨节点跑 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/RoCE 能显著缓解跨节点 AllToAll;实际仍会受拓扑、拥塞和软件栈影响,应以 NCCL/DeepEP 日志和 profiling 验证。 配置: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/。