1. EP 解决的不是“少算”,而是“专家放在哪里”

稀疏 MoE 层通常把 1 个 dense FFN 替换为大量专家网络和 1 个 router。给定 token hidden state,router 选出少量专家并给出门控权重;这降低了单个 token 的激活计算量,但模型的专家参数仍要存放在某些 GPU 上。Expert Parallelism,简称 EP,把专家 owner 分散到多个 rank,让任意 token 有机会访问整个专家池。

因此 EP 与数据并行不同。DP 复制模型、把请求分开;EP 把不同专家分片、把同一批中的 token 按路由结果交换。token 的目的地每一层都可能变化,而且不同请求、不同位置的 token 分布并不均匀。EP 的性能上限由最慢 owner rank 决定,而不是由平均 token 数决定。

2. 从 router 到专家:先建立正确的数学对象

设 batch 中有 \(N\) 个 token、专家数 \(E\)、top-k 为 \(k\)。router 产生 logits \(z \in \mathbb{R}^{N \times E}\),对每个 token 选择索引集合 \(I_t\) 与权重 \(g_{t,e}\)。输出是被选专家输出的加权和,常附加共享专家或残差支路。这里的稀疏性发生在 token 到专家的连接图,而不是在专家内部的矩阵乘法上。

\[ \begin{aligned} y_t &= \sum_{e \in I_t} g_{t,e} \cdot \operatorname{Expert}_e(x_t) \\ n_e &= \left|\left\{(t,e) : e \in I_t\right\}\right| \qquad \text{(tokens per expert)} \end{aligned} \]

critical work is controlled by \(\max_e(n_e)\), not \(\operatorname{mean}_e(n_e)\)。

路由器的 top-k 是模型语义的一部分。运行时可以重新排列 token、压缩元数据、异步发送,但不能为了均衡网络负载把 token 改送给另一个专家。负载均衡需由训练时的辅助损失、router 策略或模型规定的冗余路径来完成,不能由 serving scheduler 在无语义保证的情况下擅自完成。

3. Dispatch 与 combine:2 次 all-to-all 的含义

每个 source rank 先按目标 expert owner 对 token 编桶,形成可变长度消息。dispatch all-to-all 将 token hidden state、路由权重或必要的索引发到 owner rank;owner 将各专家的 token 排成适合 grouped GEMM 的布局,算完后再通过 combine all-to-all 把结果送回 source rank。source 依据原始 token 顺序和 gate 进行加权聚合。

all-to-all 的发送计数、接收计数、偏移和排序需要动态生成,与固定大小 all-reduce 的执行形态不同。若计数在 host 上同步,控制路径会吃掉 decode 的预算;若布局不连续,PCIe/NVLink/RDMA 传输和 GEMM 都难以满速。因此高性能实现会尽量把 metadata、token packing、传输和执行留在 GPU 异步路径上。

4. 容量因子与负载不均衡决定长尾

router 可能把大量 token 送向少数专家。训练常使用容量因子限制单 expert 每轮可接收的 token 数;过量 token 可以被丢弃、走备用路径或按模型规则处理。服务侧若假设均匀分布,只按 \(Nk/E\) 预留 buffer,真实热点就会溢出、触发临时分配或让 1 个 rank 成为整个层的 straggler。

\[ \begin{aligned} \operatorname{imbalance} &= \frac{\max_r\!\left(\mathrm{tokens\_received}[r]\right)}{\operatorname{mean}_r\!\left(\mathrm{tokens\_received}[r]\right)} \\ T_{\mathrm{layer}} &\ge \max_r\!\left(T_{\mathrm{pack}}[r] + T_{\mathrm{network}}[r] + T_{\mathrm{expert\_gemm}}[r] + T_{\mathrm{unpack}}[r]\right) \end{aligned} \]

应同时观察 expert 粒度与 rank 粒度。多个热点专家若恰好同属 1 个 rank,rank 失衡会比 expert histogram 看起来更严重;反之,专家不均匀但 owner 映射合理时仍可能可接受。prefill 的大量 token 与 decode 的小量 token 也会出现不同模式,不能只在 1 种 batch shape 上调优。

5. DeepEP 的接口思想:把通信当作可调度的算子

DeepEP 提供面向 MoE 的 dispatch/combine 通信,并区分不同拓扑和运行模式。runtime 设计者需要先理解接口边界:输入是 token 布局与路由信息,输出是 owner-local 的可计算布局。通信完成事件和 buffer 生命周期也要明确,才能与 CUDA stream 和专家 GEMM 拼成正确的依赖图。单个版本的 benchmark 不能替代这些接口语义。

1 个通信库若只提供“阻塞地把数据发完”,上层几乎无法隐藏延迟。若它暴露异步 handle、可复用 buffer、可预测的 layout 和完成信号,runtime 才能把下一批的 pack、当前批的传输、上一批的 GEMM 同时推进。性能数字会随 GPU、NIC、SM 配额、消息形状和 commit 改变,因此任何比较都应记录官方版本与命令行。

6. Token layout 是通信与算子的共同契约

专家 GEMM 希望同一专家的 token 连续,以形成较大的矩阵块;网络希望按 destination rank 连续,以形成较少的大消息;原始模型则希望输出回到 token 顺序。1 个好的 layout 通常至少维护 3 组映射:原 token 到发送位置、发送位置到 expert-local 位置、返回位置到原 token。遗漏任一映射都可能造成静默的 token 混淆,而不是显式报错。

量化和多精度会让契约更复杂。FP8 dispatch、BF16 combine 或带 scale 的 payload,都要保证 scale 和数据共享同一索引语义,对齐也不能把无关 token 混进同一块。调试可从小 batch、固定 route 和可打印 token id 开始,确认 round-trip identity 后再接入专家 GEMM。数学正确后才有必要追性能。

7. 通信计算重叠的条件,不是多开 1 条 stream

设一批 token 被切成若干 chunk。理想流水是:chunk 0 dispatch 后立即计算专家;同时 chunk 1 仍在网络上,chunk -1 的 combine 可与后续计算并行。可是重叠只有在 3 个条件同时成立时才有效:chunk 足够细以产生并发,chunk 又不能细到每次传输只剩启动开销;GPU 有足够 SM/复制引擎资源;数据依赖与 buffer 回收不会在某个 barrier 汇合。

\[ T_{\mathrm{pipelined}} \approx T_{\mathrm{warmup}} + \sum_{\mathrm{chunks}} \max\!\left(T_{\mathrm{dispatch}}, T_{\mathrm{expert}}, T_{\mathrm{combine}}\right) + T_{\mathrm{drain}} \]

而非 \(\sum_{\mathrm{chunks}} \left(T_{\mathrm{dispatch}} + T_{\mathrm{expert}} + T_{\mathrm{combine}}\right)\)。

如果 dispatch 和 grouped GEMM 都需要大量 SM,强行并发会互相降速,最终端到端更慢。若 NIC 与 GPU 的拓扑绕远,网络传输也可能抢占 HBM/NVLink 路径。正确做法是从 trace 中观察重叠区间和资源占用,扫描 chunk size、通信 SM 配额、GEMM tile,而不是只凭“理论可并行”启用重叠。

8. EP 的拓扑要求:对称性、NIC 映射和故障域

EP all-to-all 比 TP all-reduce 更容易暴露网络非对称。1 个 rank 到另一个 rank 的路径、GPU 到 NIC 的亲和性、跨 node 是否经过拥塞域,都会改变最慢消息。把所有专家均匀编号然后随意放到 GPU 上,可能让某些 owner 的高频目的地穿越更弱链路;把热专家与冷专家混排,则有机会降低最大链路负载,但必须在真实 router trace 上验证。

部署清单应包括:每个 global rank 对应的 GPU、NUMA、NIC、NVLink clique、EP group;每个方向的有效带宽与 P99;遭遇单卡或单 NIC 故障时哪些 EP group 整体不可服务。故障边界要按执行 mesh 定义,不能只按“1 个 GPU pod”口头描述。更完整的硬件视角见 Mainline 通信与拓扑

9. 在线 serving 如何把 EP 纳入调度器

调度器至少知道全局 token budget、KV 可用块和请求 deadline;对于 EP,还应收集每轮 router 输出或其可用预测,记录各 rank 的接收 token 数、dispatch/combine 字节数、等待时间和专家 GEMM 时长。注意这些信息用于排队和容量选择,而不是篡改 route。1 个长 prompt 可能在 prefill 时压垮 EP,而 decode 阶段很平稳,因而需要分别设 P/D 的 token 上限。

有前缀缓存时,路由层面的局部性并不等价于 KV 局部性。把请求迁到空闲 replica 能改善队列,却可能失去已有 KV 或需要昂贵迁移;固守 cache-local replica 又会扩大专家热点。该冲突需要由成本模型权衡,不能用“始终最短队列”或“始终本地命中”这种单条规则处理。

10. 必须采集的 profile,而不是只有 GPU 利用率

  • 每层每 EP rank 的 send/recv token 数、字节数和最大/均值比。
  • router、pack、dispatch、expert GEMM、combine、unpack 的 P50/P99 时间线。
  • 同一 stream 与不同 stream 的依赖边、event wait、空洞与 buffer reuse 次数。
  • GPU 到 NIC 的流量方向、链路利用率、重传或拥塞指标,以及对其他 collective 的干扰。
  • 按 prefill/decode、短/长上下文、不同 batch size 分桶后的性能与准确度。
  • 发生 fallback、容量溢出、临时分配、OOM 或超时的完整请求样本。

这些指标应以 trace id 和运行时版本关联,使 kernel 升级、路由器替换或环境变量变化在后续比较中都有清楚的版本边界。

11. 5 种常见失败模式

把 all-to-all 当作固定带宽问题:实际负载是可变、带元数据、带尾部的,不可用平均消息大小代替。
只优化 dispatch:combine、unpack 或最慢专家 GEMM 仍可能位于临界路径,局部加速不等于端到端加速。
chunk 过细:看似增加 overlap,实际增加 kernel launch、计数和同步次数,decode 尤其敏感。
跨拓扑组扩 EP:EP 度变大但 all-to-all 穿越弱链路,吞吐可能下降且 P99 恶化。
忽略输出正确性:索引或 gate 对齐错误能产生数值“正常”的错误文本,必须有 round-trip 与端到端回归。

12. 从零到生产的实施顺序

  1. 在单节点、小 batch 下验证 router 索引、token permutation 与 combine 的数值等价性。
  2. 测量不同 token 数、hidden size、top-k 下的 dispatch/combine 曲线,分别记录单向与双向。
  3. 将专家 GEMM 接入,确认 layout 连续性、group size 和精度路径没有破坏正确性。
  4. 增加 stream/chunk 重叠,用 trace 确认临界路径已经缩短,再调整 SM 资源。
  5. 扩到目标 EP group,验证 GPU-NIC 映射、不同方向、通信竞争和故障边界。
  6. 最后接入在线 scheduler,以真实到达 trace 扫 SLO goodput,而不是仅做饱和离线 batch。

13. 与 Mainline 的连接

EP 是 Transformer 与 MoE 在分布式系统中的落地形态;它的 token 交换由 GPU 算子 的布局和 stream 决定,又受 并行放置通信拓扑 约束。后续阅读 COMET 时,可把它看作在同一 dispatch/combine 临界路径上进一步挖掘细粒度 overlap 的方案。

14. 一手来源与版本边界

来源用途
DeepEP 官方仓库接口、支持平台、benchmark 与版本说明的主入口。
GShard / MoE 系统论文稀疏专家、通信和并行的一手背景资料。
本档案:COMET 与细粒度 overlap本文下一篇独立解释。

本页不把某次仓库 benchmark 当作通用吞吐承诺。GPU、通信库、NIC、消息形状和模型 router 的任一变化都可能改变结果。