MoE 用多个专家替换 FFN,每个 token 只走 top-k 个,计算量按激活参数算;但所有专家的权重仍要放进显存。推理 decode 的 batch 通常很小,瓶颈在权重读取而不是 FLOPs,EP 于是把整组专家分到不同 rank,用 dispatch/combine 的 all-to-all 换每卡显存。代价是通信字节数随 top-k 与 EP 增长(每 rank 的 dispatch 体积按 (EP−1)/EP 随 EP 单调增、趋近 1 倍),负载不均和小 batch 会把收益吃掉。
MoE 的稀疏性只减少激活计算,不减少总权重的常驻显存;它减少每 token 的权重读取,但所有专家仍须驻留。Mixtral 8x7B 有 47B 总参数,每 token 只激活 13B,占 27.7%;这 47B 权重都必须驻留在某处。推理 decode 的 batch 通常很小,DeepSpeed-MoE §5 把这一点说得很直接:延迟主要花在从主存加载模型参数,而不是 FLOPs。
EP 按专家切,不按张量切。一个 rank 拥有若干完整专家,token 被送到专家所在 rank。vLLM 里 ep_size = TP × DP(源码还含 PCP),开启 enable_expert_parallel 后 MoE 层不再做 TP;attention 在 TP=1 时复制、TP>1 时按 TP 切(docs/serving/expert_parallel_deployment.md:51-80)。
纯 TP+EP(DP=1、PCP=1、未开 sequence parallel)不需要 all-to-all。每个 rank 已有全部 token,只算本地专家、把非本地专家的输出置零,最后 all-reduce 求和(config.py:1066-1070、fused_moe.py:1951-1952、moe_runner.py:513-519)。只有各 rank 的 token 不同(DP>1,或 PCP>1/sequence parallel)时,才需要跨 rank 的 all-to-all dispatch/combine;否则各 rank 本地算完再 all-reduce 即可(fused_moe_modular_kernel.md:57-58)。
代价是 all-to-all。每 rank 每层的 dispatch 体积约为 B · K · H · s · (EP−1)/EP,combine 再来一遍;其中 (EP−1)/EP 随 EP 单调增、趋近 1 倍,不是线性增长。DeepSpeed-MoE 把 52B(128 个专家)模型的每卡专家从 16 降到 2(8→64 张 GPU,§5.5.1),并在 107B–2T 规模报告最高 7.3× 延迟下降(含系统优化,§5.5.2);GShard 的 dispatch+combine 占 MoE+Transformer 时间从 16% 涨到 36%(128→2048 experts,§5.2)。
读完这节你应该知道:为什么 MoE 推理的瓶颈常常是权重读取和 token 搬运,而不是矩阵乘的 FLOPs。
把 Transformer 的 FFN 换成 E 个专家,router 为每个 token 选 K 个。总参数是 Pattn + E · Pexpert,每 token 的激活参数只有 Pattn + K · Pexpert;这里 Pexpert 是单个专家(跨所有层)的参数,全部专家参数为 E · Pexpert。Mixtral 8x7B 取 E=8、K=2,§3 给出 47B 总参数、13B 激活参数;架构表 Table 1 则给出 dim=4096、hidden_dim=14336、n_layers=32 等配置。这些数字都不是本页估算。
Pexpert 为单个专家参数。公式只拆参数,不含 KV cache、激活与通信缓冲。这三条式子给出三个不同的成本方向:显存看总参数,计算看激活参数,通信看每个 token 要被复制到几个专家。MoE 让第二项变小,却不会自动让第一项变小。
专家再多,未被选中的专家也要常驻。EP/TP 降低的是每卡专家权重,不是总权重;attention 与 KV cache 另算。
激活 FLOPs 约为 dense FFN 的 K/E。但 decode 时 batch 小,矩阵乘的算术强度低,省下的 FLOPs 不一定换成等比例的延迟下降。
每个 token 产生 K 份专家输入。专家不在本 rank 时,这份输入要跨卡;EP 的 all-to-all 体积正比于 K · B · H。
DeepSpeed-MoE §5 的观察是:“During inference, the batch size is generally small, so the inference latency of an MoE model depends primarily on the time it takes to load the model parameters from the main memory.” 这句话把讨论从「MoE 计算少所以快」拉到「每卡要读多少权重、读完之后还要搬多少 token」。后面的显存与通信估算都围绕这两件事。
Mixtral 论文在 §3 也提醒了同一件事:13B 激活参数只决定推理计算成本,服务 Mixtral 的显存成本按 47B 稀疏参数计;SMoE 的额外开销来自 routing 与「一张卡上放多个专家」时的内存读取,更适合有 batch 的工作负载。§3
读完这节你应该知道:两种并行在显存、计算和通信上的差别,以及 vLLM 如何把 attention TP 与 MoE EP 组合起来。
MoE 层沿用 dense 的 TP:每个专家的 W1/W2/W3(gate/up/down)张量按 TP 切分,每张卡存每个专家的 1/TP。一个 token 的 top-k 专家计算被拆到 TP 个 rank 上,down_proj 之后做 all-reduce 把部分和加起来。
纯 TP(DP=1)下每卡专家权重约为 E · Pexpert/TP(每个专家都被切,但每卡仍要存所有专家的切片);层内通信是 [B, H] 的 all-reduce。没有路由相关的 all-to-all,但每个 rank 都要参与每个 token 的计算。若 DP>1 且未开 EP,vLLM 会把 MoE 的 TP 组展平成 TP×DP,每卡专家权重约为 E · Pexpert/(TP×DP);attention 仍在每个 DP 组内按 TP 切。
vLLM 未开启 EP 时,MoE 层用 TP × DP(源码还含 PCP)的扁平 TP 组(expert_parallel_deployment.md:82-83)。
专家按 rank 切开:一个 rank 拥有若干完整专家,token 只被送到被选中专家所在的 rank。每卡专家权重约为 E · Pexpert/EP;DP+EP 时换来 dispatch/combine 的 all-to-all,DP=1 时只在本 rank 算完再做 all-reduce。
attention 不受 EP 影响:TP=1 时 attention 权重在每个 DP rank 复制;TP>1 时在每个 DP 组内按 TP 切。EP size 取 TP × DP(源码还含 PCP)。
vLLM 的 EP 组覆盖 TP×DP(源码还含 PCP)个 rank;MoE 层内 tp_size 置 1(config.py:1142-1151,1264-1272)。
表 2-1:TP-only 与 EP 的对照。TP 列按 vLLM 的 MoE TP 行为(纯 TP 与未开 EP 的 DP 组合分开写);EP 列按 expert_parallel_deployment.md:63-83 与 config.py:1142-1151,1264-1272。两列都假设专家权重为 BF16,忽略 KV cache、激活和通信缓冲。
| 维度 | TP-only(MoE 层) | EP(MoE 层) |
|---|---|---|
| 切分对象 | 每个专家的张量 | 整组专家 |
| 每卡专家权重 | E · Pexpert / TP(纯 TP);未开 EP 的 DP 组合为 E · Pexpert / (TP×DP) | E · Pexpert / EP |
| 每卡 attention | 1/TP(attention 也做 TP) | TP=1 时复制;TP>1 时 1/TP |
| 层内通信 | [B, H] all-reduce | DP>1:K · B · H 量级 all-to-all;DP=1:all-reduce |
| 是否依赖路由 | 不依赖;所有 rank 算所有 token 的切片 | 依赖;token 只去被选中专家所在的 rank |
| 典型组合 | dense 模型或未开 EP 的 MoE | attention TP + MoE EP;或 MoE 层单独用 EP(TP=1) |
GShard 把「专家分片、attention 复制」写进 SPMD 切分:MoE 层把专家 shard 到很多设备上,非 MoE 层走另一套切分(§3.1–§3.2);MoE 层的 dispatch 与 combine 用 AllToAll 完成(§5.2)。DeepSpeed-MoE 在推理侧把 tensor-slicing 与 expert-slicing 组合:Table 6 的 52B 模型用 MP=1、EP degree=128,349B 模型用 MP=4、EP degree=128,1T 级模型用 MP=8、EP degree=128(Table 6)。注意 Table 6 是模型配置的 EP degree;同一篇 §5.5.1 的扩展实验实际把 128 个专家放到 8 张 GPU(每卡 16 个)再到 64 张(每卡 2 个),两者不要混成同一个部署规模(§5.5.1)。
两种并行不是替代关系。EP 把「专家权重」这一项除以 EP,但 attention/KV cache 在 TP=1 时没有变小;TP 把 attention 和专家都切细,但每个 token 的每次 FFN 都要 all-reduce。vLLM 开启 EP 后的组合是:attention 继续用 TP,MoE 层用 EP,EP size 取 TP×DP(源码还含 PCP);注意 EP 默认关闭(enable_expert_parallel=False),不是不配置就自动启用。
读完这节你应该知道:top-k 路由、专家 FFN 与 EP 布局各自做什么,以及 vLLM 的 fused MoE 把哪些步骤合在一起。
Mixtral 的 MoE 输出可以写成两项:router 对每个 token 算 8 个 logits,取 top-2 后做 softmax,得到两个权重;两个 SwiGLU 专家的输出按权重相加(§2.1)。
vLLM 的 fused_topk 支持 softmax 与 sigmoid 两种评分,并可选择 renormalize;Mixtral 在 vLLM 里固定 renormalize=True(fused_topk_router.py:160-205、mixtral.py:124)。DeepSeek 系列走 grouped_topk:先按 group 选 top-k 组,再在组内选 expert;带 e_score_correction_bias 时,bias 只参与「选哪些专家」,权重仍取无偏分数(grouped_topk_router.py:92-178)。
表 3-1:两种路由配置。Mixtral 数字来自 Mixtral §2.1/Table 1 与 vLLM mixtral.py:124;DeepSeek-V3 数字来自 DeepSeek-V3 §2.1.2 Eq.12–16 与 §4.2 Hyper-Parameters。
| 模型 | 评分 | 每 token 选择 | 专家数 | 特殊处理 |
|---|---|---|---|---|
| Mixtral 8x7B | softmax | top-2 | 8 | vLLM 中 renormalize=True;两个 SwiGLU 输出按权重相加 |
| DeepSeek-V3 | sigmoid + bias | 8 个 routed(另加 1 个 shared) | 256 routed + 1 shared | bias 只用于选择;权重用无偏 sigmoid 分数;shared expert 恒开 |
EP 下每个全局专家 id 需要映射到本 rank 的本地 id。vLLM 用 expert_map:本地专家的位置写 0..local_num_experts−1,不在本 rank 的专家写 −1(expert_map_manager.py:66-87)。这个 −1 不是「丢弃 token」,而是告诉 kernel「这个专家不归我算」;该 token 在这一份专家上的本地贡献为 0,由真正拥有专家的 rank 计算。
布局有两种。默认 linear 是连续切:4 个专家、2 个 rank 时,rank 0 拿 [0,1]、rank 1 拿 [2,3];round_robin 是交错切:rank 0 拿 [0,2]、rank 1 拿 [1,3](parallel.py:219-228)。round_robin 只对多 expert group、且无冗余专家/EPLB 的模型生效;在走 all-to-all kernel 的配置里,还要求后端为 DeepEP low-latency 或 NIXL EP,否则回退 linear(expert_map_manager.py:116-149)。除不尽时,前 remainder 个 rank 各多拿一个专家(expert_map_manager.py:66-76);EPLB 则要求加上冗余专家后的总数能被 EP size 整除,否则启动报错(layer.py:278-283)。
真正走不走网络,取决于 DP(以及 PCP/sequence parallel)。纯 TP+EP(DP=1、PCP=1、未开 sequence parallel)时每个 rank 已经有全部 token,不需要 dispatch:各 rank 只算本地专家,非本地输出置零,最后 all-reduce 求和。DP+EP 时每个 rank 的 token 不同,才需要 prepare 阶段的 all-to-all 把 token 送到专家所在 rank,finalize 阶段再把结果 combine 回来。
每个被选中的专家是一组 SwiGLU FFN:W1/W3 做 gate/up,激活后 W2 做 down。Mixtral 的两个专家输出按 router 权重相加;DeepSeek-V3 在此基础上多一个恒开的 shared expert,输出是 ut + Σ shared + Σ routed(§2.1.2 Eq.12)。架构上 shared expert 不进 router 的 top-k(Eq.12);DeepSeek-V3 在 decode 部署中把它当作 routed one(§3.4.2),每 token 9 个,shared 必选。在 vLLM 里它可能是独立 MLP,也可能通过 fuse_shared_experts 并进 FusedMoE(n_shared_experts),具体放置与切分取决于实现(deepseek_v2.py:345-399)。
combine 要做两件事:按 gi 加权,再把 K 份结果求和。vLLM 的 moe_sum 负责求和;权重可以在专家 kernel 内乘,也可以在 combine 前用 TopKWeightAndReduce 乘,取决于 experts 实现是否已经做了 weight+reduce(topk_weight_and_reduce.py:59-120)。
「fused MoE」指的是把 routing、token 排序/填充、grouped GEMM、激活与 top-k reduction 尽量合进少量 kernel,不是说 EP 的通信被省掉了。EP 的通信(DP+EP 的 all-to-all,或 DP=1 时的 all-reduce)仍发生在 MoE 层的边界上。
读完这节你应该知道:每卡专家显存和 all-to-all 字节数怎么估,以及 EP 与 TP 组合时谁负责哪一层。
第一条是显存账:专家权重按 EP 均分,但 attention、router、embedding 不按 EP 分(TP=1 时复制)。第二条是通信账(直接 all-to-all):一个 token 有 K 份专家输入,其中约 (EP−1)/EP 要离开本 rank;H 是 hidden size。第三条把 dispatch 与 combine 都算上。三个式子都只给数量级,真实实现还受路由倾斜、padding、量化与后端影响;默认后端的物理字节数会高于这里的下界(§4.4)。
常量:H=4096、K=2、32 层来自 Mixtral Table 1;BF16(2 B)为本页假设。EP size 取 2 的幂;B 是每 rank 的 token 数。读数按直接 all-to-all 的发送体积(下界)计算,未含后端复制放大(尤其 allgather_reducescatter)。Mixtral 只有 8 个专家,EP>8 时部分 rank 没有专家,滑块仍按均匀公式展示,仅作数量级示意。
组 1:B=512、EP=8。每 token 的 K=2 份专家输入,每份 H=4096 个 BF16 元素。每 rank 的 dispatch 体积:
512 × 2 × 4096 × 2 × (7/8) = 7,340,032 B = 7.0 MiB。
dispatch+combine:7.0 × 2 = 14.0 MiB。32 层一次前向:14.0 × 32 = 448.0 MiB。
组 2:B=2048、EP=8。2048 × 2 × 4096 × 2 × (7/8) = 29,360,128 B = 28.0 MiB;dispatch+combine 28.0 × 2 = 56.0 MiB;32 层 56.0 × 32 = 1792.0 MiB。JS 在 B=512/EP=8 与 B=2048/EP=8 下分别输出 7.0/14.0/448.0 与 28.0/56.0/1792.0,口径按 MiB(÷220)。
简化与未覆盖。假设每个 token 的 K 个专家均匀落在不同 rank、每个 rank 的 token 数相同;没有计 all-to-all 的 metadata、量化 scale、padding 与重排缓冲,也没有计后端复制放大(尤其 allgather_reducescatter 会把每个 rank 的 token 复制到所有 rank)。没有覆盖 Switch 式 capacity factor 与 drop/overflow、冗余专家/EPLB、shared expert,也没有覆盖 TP 的 all-reduce 与 attention 的通信;也没有覆盖 EP>E(部分 rank 没有专家)的情况;这些都会改变实际字节数。
条形:专家权重(陶土红)/ 复制的 attention+router+embedding(青灰)。不含 KV cache、激活、通信缓冲、冗余专家与 LM head(若未与 embedding tie)。EP>8 时部分 rank 没有专家,滑块仍按均分公式展示,仅作数量级示意。
先拆参数。Mixtral Table 1 给 dim=4096、hidden_dim=14336、n_layers=32、num_experts=8、n_kv_heads=8、head_dim=128、vocab_size=32000。每个 SwiGLU 专家有 3 个矩阵(gate、up、down);全部专家参数(E · Pexpert,E=8):4096 × 14336 × 3 × 8 × 32 = 45,097,156,608 ≈ 45.10B。
复制部分:attention 每层是 Q/O 两个 4096×4096 加 K/V 两个 4096×1024,32 层为 (2×4096×4096 + 2×4096×1024) × 32 = 1,342,177,280;router 为 4096×8×32 = 1,048,576;embedding 为 32000×4096 = 131,072,000;合计 1,474,297,856 ≈ 1.474B。每 token 激活参数约为 2×4096×14336×3×32 + 1.474B = 12.75B,与论文四舍五入的 13B 对得上。总参数 45.10B + 1.474B = 46.57B,加上可能的独立 LM head(未计)与取整,约等于论文的 47B。
组 1:EP=8。专家权重 BF16:45,097,156,608 × 2 / 8 / 230 = 10.50 GiB;复制部分 1,474,297,856 × 2 / 230 = 2.746 GiB ≈ 2.75 GiB;合计 10.50 + 2.75 = 13.25 GiB。
组 2:EP=16。专家权重 45,097,156,608 × 2 / 16 / 230 = 5.25 GiB;加同样的 2.75 GiB 复制部分,合计 8.00 GiB。JS 在 EP=8 输出 10.50/2.75/13.25,在 EP=16 输出 5.25/2.75/8.00,单位 GiB(÷230)。
简化与未覆盖。按 BF16 与 TP=1 估算,所以 attention/router/embedding 视为每卡复制;TP>1 时这部分会按 TP 切小。未计 KV cache、激活、all-to-all 缓冲、量化 scale、冗余专家/EPLB 与 shared expert,也未计 LM head(若未与 embedding tie);也未计 checkpoint 的 padding 与未使用参数,以及 EP>8 时部分 rank 没有专家的情况。Mixtral 论文只给 47B/13B 的整数,45.10B/12.75B 是由 Table 1 配置推算的值。
vLLM 文档把 EP size 定义成 TP_SIZE × DP_SIZE,并给出 attention 的两种行为:TP=1 时 attention 权重在所有 DP rank 上复制;TP>1 时在每个 DP 组内按 TP 切(expert_parallel_deployment.md:51-80)。源码里对应的动作是:ep_size = flatten_tp_size = DP × PCP × TP,MoE 层内的 tp_size 置 1(config.py:1142-1151,1264-1272)。
表 4-1:EP 与 TP/DP 的组合。前两行有 DP>1,MoE 层需要 dispatch/combine;第三行 DP=1、PCP=1 且未开 sequence parallel,按源码 use_all2all_kernels=False,不需要 all-to-all。
| TP | DP | EP | attention | MoE 专家 | MoE all-to-all |
|---|---|---|---|---|---|
| 1 | 8 | 8 | 每个 DP rank 复制一份 | 8 个 rank 各持有 1/8 专家 | 需要(DP>1) |
| 2 | 4 | 8 | 每个 DP 组内 TP=2 | 8 个 rank 各持有 1/8 专家 | 需要(DP>1) |
| 8 | 1 | 8 | TP=8 | 8 个 rank 各持有 1/8 专家 | 不需要(DP=1、未开 SP) |
第三行容易被忽略:EP 已经把专家分开,但每个 rank 仍拿到全部 token,于是每个 rank 只算自己那 1/8 专家,其余专家的输出置零,最后用 all-reduce 把 8 份部分和加起来。vLLM 的判断条件是 use_ep and (dp_size > 1 or pcp_size > 1 or is_sequence_parallel)(config.py:1066-1070);不满足时不会走 all-to-all kernel:默认 allow_new_interface=False 返回 None;其他分支分别走本地重排(XPU/NoDPEP)或 allgather+reduce-scatter 的 DP 回退(all2all_utils.py:204-232)。
vLLM 的默认后端是 allgather_reducescatter:用 allgather + reduce-scatter 实现 all-to-all,适用于一般 EP+DP 配置。多节点 prefill 常用 deepep_high_throughput,decode 常用 deepep_low_latency;passthrough 则是 MoE 后端自己 dispatch/combine,框架层不再执行 all-to-all(expert_parallel_deployment.md:35-45)。
表 4-2:常用 all2all 后端。表内“适用”列来自 vLLM 文档,不含延迟/吞吐实测。
| 后端 | 适用场景 | 要点 |
|---|---|---|
allgather_reducescatter | 通用 EP+DP | 默认后端;用 allgather/reduce-scatter 原语实现 all-to-all |
deepep_high_throughput | 多节点 prefill | grouped GEMM + continuous layout,偏吞吐 |
deepep_low_latency | 多节点 decode | CUDA graph、masked layout,偏低延迟 |
passthrough | MoE 后端自带 dispatch/combine | 框架层不跑 all-to-all;CLI 不可直接选 |
后端会改变物理字节数,不能直接套用上面的直接 all-to-all 公式。vLLM 的默认后端是 allgather_reducescatter(parallel.py:229):dispatch 用 all_gatherv、combine 用 reduce_scatterv(all2all.py:138,162)。它先把每个 rank 的 token 收集到所有 rank——allgather 后每 rank 持有全部 token 的副本——再由 reduce-scatter 把各 rank 的部分输出求和并按 rank 切回。按均匀路由估算,每 rank 每层的物理 dispatch 体积约为 (EP−1) · B · H · s,是直接 all-to-all 发送体积 B · K · H · s · (EP−1)/EP 的 EP/K 倍;Mixtral 的 EP=8、K=2 就是 4 倍。以 B=512、H=4096、BF16 为例,每 rank 每层约 28.0 MiB((8−1) × 512 × 4096 × 2),dispatch+combine 56.0 MiB,32 层一次前向 1792.0 MiB;B=2048 时分别为 112.0/224.0/7168.0 MiB。直接 all-to-all 只发目标 rank;DeepEP 一类实现还会做量化、异步与 layout 变换。比较不同后端的延迟时,必须同时看 batch、序列长度、EP size 与网络(NVLink/IB),不能只拿一个倍率横比。
读完这节你应该知道:vLLM 一次 MoE 前向在纯 TP+EP 与 DP+EP 下分别走哪条路径,以及 all-reduce 与 all-to-all 的分界。
g_i 加权求和。实线强调示意 token 选中的 E0/E2,虚线框是 EP 的 rank 边界;纯 TP+EP 且 DP=1、未开 SP/PCP 时,dispatch 退化为本地 expert_map,没有跨卡 all-to-all。把这张图落到 vLLM 的代码路径,一次 MoE 前向分五步:
fused_topk 或 grouped_topk 输出 topk_weights 与 topk_ids(fused_topk_router.py:160-205)。expert_map 把本 rank 的专家映射到 0..local_num_experts−1,其余写 −1(expert_map_manager.py:66-87)。moe_align_block_size 按专家把 token-expert 对排序、按 block 对齐并填充;随后是第一组 GEMM(W1/W3)、激活、第二组 GEMM(W2)(fused_moe.py:1708-1714,1888-1954)。expert_map 时先把输出 buffer 清零,kernel 跳过 −1 的专家;这样本地只留下「本 rank 拥有的专家」的贡献(fused_moe.py:1951-1952)。moe_sum 把 top-k 输出加起来;之后按路径分叉:纯 TP+EP(DP=1、PCP=1、未开 SP)不做 all-to-all,最后在 tp_size>1 或 ep_size>1 时做 all-reduce(moe_runner.py:513-519);DP+EP 则由 prepare 做 dispatch、finalize 做 combine(modular_kernel.py:331-451)。两条路径的分界是 DP(以及 PCP/SP),不是 EP。EP 只说明「专家被分到不同 rank」;如果 DP=1 且没有 PCP/sequence parallel,每个 rank 仍拿到同一批 token,本地算完再 all-reduce 即可,不需要把 token 发来发去。只有每个 rank 的 token 不同(DP>1,或 PCP>1/sequence parallel)时,才必须用 dispatch/combine 把 token 送到专家所在 rank。这个区别在 vLLM 里由 use_all2all_kernels 判断(config.py:1066-1070)。
shared expert 是另一条支路:DeepSeek-V3 的 shared expert 恒开,输出直接加到 routed 输出上(§2.1.2 Eq.12);vLLM 的 async all2all 后端可以在 combine 期间 overlap shared expert 计算(moe_kernel_features.md:15)。这条支路在架构上不进 router 的 top-k(Eq.12);DeepSeek-V3 在 decode 部署中把它当作 routed one(§3.4.2),每 token 9 个,shared 必选。它在 EP 部署里也可以被放到不同 GPU(DeepSeek-V3 decode 就用 64 张 GPU 承载冗余与 shared),不能简单把它当作每卡复制的 dense FFN。
读完这节你应该知道:哪些数字是端到端实测、哪些只是部署配置,以及它们各自的模型与并行设置。
表 6-1:论文/文档中的 EP 相关实测与部署配置。每行都注明模型、并行配置与指标口径;不同来源的硬件、batch、baseline 不同,不能直接横向比较。
| 来源 | 模型 / 规模 | 并行配置 | 结果 | 口径与限制 |
|---|---|---|---|---|
| DeepSpeed-MoE §5.5.1/Table 6 | 1.3B+MoE-128(52B) | Table 6:EP degree=128、MP=1;扩展实验:8→64 张 A100 | 每卡专家 16→2;总吞吐随 GPU 数增加,单卡吞吐也上升(super-linear) | 与 PyTorch 基线比;不是单独拆出 EP 的消融,含通信与 kernel 优化 |
| DeepSpeed-MoE §5.5.2/Table 6 | 107B–2T MoE | Table 6 的 EP degree=128;最多 256 张 A100 | 延迟最高降 7.3×、吞吐最高升 7.3×;万亿参数模型延迟 <25 ms | vs PyTorch 基线;含系统优化,不能当作纯 EP 的加速比 |
| GShard Table 1/§5.2/§5.3 | 600B MoE(2048E, 36L) | 2048 个 TPU v3 core | 专家从 128 扩到 2048(16×)时设备时间 ×1.7;dispatch+combine 占 MoE+Transformer 时间 16%→36%;AllToAll 约 O(√D) | 训练侧微基准,不是推理结论;硬件为 TPU |
| DeepSeek-V3 §3.4(部署)/§4.2(模型参数) | 671B 总 / 37B 激活;256 routed + 1 shared | prefill:32 GPU,TP4+SP、DP8、EP32;decode:320 GPU,TP4+SP、DP80、EP320 | prefill 每卡 8 个 routed + 1 个冗余;decode 每卡 1 个专家,64 张 GPU 承载冗余与 shared;decode 每专家 batch 通常 ≤256 token | 部署配置;论文未给端到端延迟/吞吐数字 |
| Mixtral Table 1/§3 | 47B 总 / 13B 激活;8 experts、top-2 | 架构数字,未绑定并行配置 | 激活参数占 27.7%;显存成本按 47B 稀疏参数计 | 论文没有给 EP 下的推理实测;不能把架构比例当加速比 |
| vLLM 文档 | DeepSeek-V3-0324 | TP=1、DP=8、EP=8;8 张 H200/H20 | EP size = TP×DP = 8;attention 复制、专家分片 | 只给配置,不给延迟/吞吐数字 |
最反直觉的是吞吐可以超线性。DeepSpeed-MoE 把 52B、128 专家的模型从 8 张 GPU 扩到 64 张:每卡专家从 16 降到 2,单卡要读的专家权重变少,于是单卡吞吐上升,总吞吐超线性(§5.5.1)。这不是「更多 GPU 一定更快」;论文同时指出 GPU 变多会增加专家间的 all-to-all,只有通信优化跟得上,数据局部性的收益才会兑现。
GShard 给出的是通信占比,不是端到端加速。专家从 128 扩到 2048,dispatch+combine 的执行时间约涨 3.75×,占 MoE+Transformer 的比例从 16% 升到 36%(§5.2);AllToAll 在 2D TPU 网络上约为 O(√D),2048 个分区相对 16 个分区,D 涨 128×,执行时间只涨约 9×(§5.3)。这些是训练微基准,不应直接当作推理延迟。
DeepSeek-V3 的推理部署是「小 batch、按专家拆」的极端案例。decode 阶段用 320 张 GPU 做 EP320,每卡只放 1 个专家,64 张 GPU 专门承载冗余专家与 shared expert;每专家 batch 通常不超过 256 token,瓶颈是内存访问而不是计算(§3.4.2)。论文给的是部署结构与 batch 规模,没有给端到端延迟。
不要把 7.3×、1.7×、27.7% 放在一起比。7.3× 是 DeepSpeed-MoE 整套系统相对 PyTorch 的延迟/吞吐,1.7× 是 GShard 训练侧设备时间,27.7% 是 Mixtral 的激活参数占比;三者的分母、硬件与 baseline 都不同。页面把它们并列,是为了说明不同层面的成本,而不是排一个加速榜。
读完这节你应该知道:EP 在哪些条件下会把收益吐回去,以及哪些数字不能从训练直接搬到推理。
router 若把大量 token 送给少数专家,这些 rank 的 GEMM 与显存带宽先饱和,其他 rank 等 all-to-all。EPLB 用冗余专家复制热专家,并按窗口统计重排;vLLM 的 EPLB 要求加上冗余专家后的总数能被 EP size 整除(layer.py:278-283)。
GShard 在 2048 experts 时 dispatch+combine 占 MoE+Transformer 时间 36%,128 experts 时为 16%(§5.2)。DeepSeek-V3 训练时计算/通信比约 1:1,才需要 DualPipe 重叠,并把每个 token 限制在最多 4 个节点(§3.2.1-§3.2.2)。
decode 每 token 只读少量权重,batch 小,延迟由权重读取与 all-to-all 固定开销主导。Mixtral §3 也指出 SMoE 更适合有 batch 的工作负载;batch 太小时,多个专家/卡的读取与路由开销会吃掉稀疏计算的收益。
并行度约束。EP size 由 TP × DP(源码还含 PCP)决定,专家数除以 EP 得到每卡专家数;EP 大于专家数时,部分 rank 的 local_num_experts 会变成 0(expert_map_manager.py:66-76),这些 rank 对 MoE 专家分片没有收益(仍可能承载 attention/其他层)。TP=1 时 attention 在每个 DP rank 复制,EP 不降低 attention/KV cache 的显存;所以「每卡能放下」要同时看专家权重、attention 与 KV cache,不能只算专家。
capacity factor 与 drop 是训练侧机制。Switch 用静态 shape 的 expert capacity:capacity = (tokens per batch / number of experts) × capacity factor,超过容量的 token 走残差、不经过专家;在带辅助负载均衡损失的训练里,drop 通常低于 1%(§2.2 Eq.3)。DeepSeek-V3 明确写了训练与推理都不 drop token(§2.1.2)。本页核对的 vLLM Triton fused MoE 路径没有 capacity factor 参数:token-expert 对按专家排序、按 block 对齐并填充,没有 Switch 式的按容量丢弃;不同 all2all 后端可能有自己的缓冲上限,本页未逐一验证。把 Switch 的训练 drop 率直接搬到 vLLM 推理,是不成立的。
冗余专家不是免费的。EPLB 复制热专家会增加每卡权重与显存占用;vLLM 的 num_redundant_experts 只在 EPLB 开启时允许非零(layer.py:285-288),未显式指定时默认取最小的冗余数,使「逻辑专家数 + 冗余数」能被 EP size 整除(parallel.py:652-676)。DeepSeek-V3 在 prefill 用 32 个冗余专家、每卡 8+1 个(§3.4.1),decode 用 64 张 GPU 承载冗余与 shared(§3.4.2);这些都是模型侧部署选择,不是 vLLM 的默认行为。
最后,EP 只解决 MoE 层的专家权重。attention、dense 层、embedding 与 KV cache 不因 EP 变小;网络拓扑也会改变结论:DeepSeek-V3 的集群里 NVLink 约 160 GB/s,IB 约 50 GB/s,相差约 3.2×,所以它把 token 限制在最多 4 个节点,跨节点走 IB、节点内走 NVLink(§3.2.2)。换成跨节点只能走 IB、没有 NVLink 的集群,同样的 EP size 可能得到完全不同的结果。