训练系统与 Kernel / 并行与通信 实现解析进阶 4 分钟阅读 查看源文件 ↗

DeepEP#

本页内容9 个部分
查看概览图
MOE EXPERT PARALLEL COMMUNICATION

把慢链路的用量
压到每节点一次

两个 all-to-all(dispatch / combine)是大规模 MoE 的主要通信瓶颈。此图讲解 DeepEP V1 的 normal / low-latency 两条路径;V2 的通信后端和接口已有变化。

ROUTER top-k 每个 token 选 k 个专家
DISPATCH all-to-all 送到持有专家的 GPU
EXPERT Grouped GEMM 每专家 token 数不等
COMBINE all-to-all 按 gating 加权求和
token 去哪个 GPU 是数据相关、动态的,而且每张卡都要同时收发
①带宽压力

每层每 token 都要搬运 hidden——prefill / 训练时 token 海量。

②延迟压力

decode 阶段每步 token 很少,all-to-all 的固定延迟被放大。

③拓扑非对称

节点内 NVLink 快(~150GB/s 级),跨节点 RDMA / IB 慢得多。

核心技巧
非对称域带宽转发

跨节点:一个 token 去某个目标节点只走 1 次 RDMA,发到那个节点里的「一个」GPU;节点内:再用快的 NVLink 转发到具体的专家 GPU。把慢链路的用量压到每 token 每目标节点最多一次,剩下的扩散都交给快链路。

01Normal kernels

高吞吐 · 训练 / 推理 prefill

  • NVLink + RDMA 同时用
    双链路
  • 非对称域带宽转发见上
  • 配合 group-limited 路由每 token 最多发到 ≤ M 个节点,跨节点流量可控
  • FP8 dispatch通信字节直接减半
    低精度
两类目标 吞吐 vs 延迟
02Low-latency kernels

低延迟 · 推理 decode

  • 纯 RDMA为最小化延迟而设计
    单链路
  • hook 式通信-计算重叠RDMA 异步发起,hook 在后续计算时收尾
  • V1 低延迟路径支持不占用 SM 的重叠仍占用网络、显存带宽等资源;V2 已改变通信实现
    关键

combine 是 dispatch 的逆过程:专家输出沿原路返回,最后按 gating 权重加权求和。group-limited 路由限制每 token 的目标节点数 ≤ M,转发量因此有上界。

训练 / prefill · 高吞吐套件decode · 低延迟套件大规模 MoE 的通信地基

版本范围:本文主要解释 DeepEP V1 的 normal / low-latency 设计。 截至 2026-09-29,官方 V2 已统一为 ElasticBuffer 并使用 NCCL Gin;V1 的“0 SM low-latency EP”不能套到 V2。参见当前仓库与V1 文档。

DeepEP 是 DeepSeek 开源的 专家并行(Expert Parallelism, EP)通信库,专门优化 MoE 里 token 在多 GPU/多节点之间散发与收回的 all-to-all 通信。

一句话版本:

MoE 把专家拆到很多 GPU 上,每个 token 要被送到它命中的那几个专家所在的 GPU(dispatch),算完再送回来按权重相加(combine)。这个 all-to-all 是大规模 MoE 的主要通信瓶颈,DeepEP 提供了高吞吐和低延迟两套 GPU kernel 把它打满。


1. 背景:MoE 专家并行下的 all-to-all#

MoE 的专家很多、很大,单卡放不下,于是按 专家并行 把专家分散到多张 GPU(甚至多节点)。一层 MoE 的执行流程:

router 给每个 token 选 top-k 个专家
        ↓  dispatch(散发)
把 token 的 hidden 送到「持有目标专家」的那些 GPU         ← all-to-all
        ↓
各 GPU 上专家算 FFN(每个专家收到的 token 数不等 → Grouped GEMM)
        ↓  combine(收回)
把专家输出送回原 GPU,按 gating 权重加权求和               ← all-to-all

两个 all-to-all(dispatch / combine)就是通信热点。token 去哪个 GPU 是 数据相关、动态 的,而且每张卡都要同时收发——这正是 all-to-all 难做快的原因。

专家侧那一步「变长 matmul」就是 Grouped GEMM;DeepEP 管的是它前后的通信。


2. 先补 baseline:All-to-All 到底是什么#

在讲 DeepEP 怎么优化之前,得先搞清楚它优化的对象——all-to-all 这个集合通信原语。

把它和常见的两个原语放一起对比最清楚(设 N 张卡,每张卡本地有一份数据):

All-Gather :每卡把「同一份」数据发给所有卡 → 人人拿到拼接结果
All-Reduce :每卡的数据按位相加(求和/求平均)→ 人人拿到同一个归约结果
All-to-All :每卡发给「每张卡各不相同」的一份数据(个性化)← MoE 用的是它

All-to-All 的本质是一次 跨卡数据转置。把每卡的发送缓冲切成 N 段,第 i 卡的第 j 段是「要发给第 j 卡的数据」:

              发送方 rank i →
            ┌──────┬──────┬──────┐            ┌──────┬──────┬──────┐
   rank 0   │ 0→0  │ 0→1  │ 0→2  │            │ 0→0  │ 1→0  │ 2→0  │  rank 0 收
   rank 1   │ 1→0  │ 1→1  │ 1→2  │   ──转置──▶ │ 0→1  │ 1→1  │ 2→1  │  rank 1 收
   rank 2   │ 2→0  │ 2→1  │ 2→2  │            │ 0→2  │ 1→2  │ 2→2  │  rank 2 收
            └──────┴──────┴──────┘            └──────┴──────┴──────┘
             (i→j = i 发给 j 的段)             收完后 rank j 手里是所有 i→j

在 MoE 里,「i→j 段」就是 rank i 上被路由到 rank j 那些专家的 token。dispatch 是一次 all-to-all,combine 是它的逆转置。


3. 朴素 all-to-all 怎么做、为什么慢#

① 变长:得先换一次「元数据」。 普通 all-to-all(如 MPI_Alltoall)假设每段等长;但 MoE 里每卡发给每卡的 token 数是数据相关、运行时才知道的。所以标准套路是两趟:

第 1 趟:all-to-all 交换「计数」—— 每卡告诉每卡「我要发你几个 token」
         (据此各卡才能算出接收 offset、分配接收缓冲)
第 2 趟:all-to-all 交换「真正的 token hidden」

V1 的 get_dispatch_layout 计算本地路由布局;跨 rank 协调和 handle 的生成还涉及 dispatch 协议,不能把这个本地调用直接等同于一次全局计数 all-to-all。

② 朴素实现:每对卡直接点对点。 最直接的算法是 N-1 步,每步 rank i 和某个 rank 交换一段(例如 rank 数为 2 的幂时可使用 XOR 配对):


# 伪代码:先投递非阻塞收发,再统一等待;不要让所有 rank 阻塞 send 后才 recv
requests = []
for peer in range(N):
    if peer != rank:
        requests += [irecv(recvbuf[peer], from_rank=peer),
                     isend(sendbuf[peer], to_rank=peer)]
wait_all(requests)

③ 为什么它可能成为通信瓶颈。 不同 collective 的成本必须在相同数据量和拓扑下比较,不能普遍排序。All-to-all 是全体对全体的个性化交换,直接压在网络的 对分带宽(bisection bandwidth) 上——这是整个网络最容易被打爆的地方。每卡要发出的总量 ≈ (N-1)×每段,N 越大、跨的交换机越多,越慢。

④ 放到真实拓扑上更糟。 朴素做法对「一个 token 命中的每个跨节点专家都单独发一份 RDMA」,慢链路(跨节点 IB/RDMA)被反复打。这正是 DeepEP 第 5 节要解决的痛点。


4. 为什么难:带宽 + 延迟 + 非对称拓扑#

带宽压力:每层每 token 都要搬运 hidden(prefill/训练时 token 海量)
延迟压力:decode 阶段每步 token 很少,all-to-all 的固定延迟被放大
拓扑非对称:节点内 NVLink 快(~150GB/s 级),跨节点 RDMA/IB 慢得多

所以「训练/prefill」和「decode」是两类完全不同的优化目标:前者要 吞吐,后者要 低延迟。DeepEP 因此给了两套 kernel。


5. 两套 kernel#

5.1 Normal kernels(高吞吐)—— 训练 / 推理 prefill#

  • 同时用 节点内 NVLink + 跨节点 RDMA;
  • 核心优化是 非对称域带宽转发(asymmetric-domain bandwidth forwarding)(见第 6 节);
  • 配合 DeepSeek-V3 的 group-limited 路由(每个 token 最多发到 ≤ M 个节点),让跨节点流量可控;
  • 支持 FP8 dispatch:散发时用低精度,通信字节直接减半。

5.2 Low-latency kernels(低延迟)—— 推理 decode#

  • 纯 RDMA,为最小化延迟而设计;
  • hook 式通信-计算重叠:通信用 RDMA 异步发起,用一个 hook 在后续计算时收尾,从而把 all-to-all 藏到 attention/MLP 计算后面;
  • 关键点:通信不占用 SM。普通 all-to-all kernel 会吃 GPU 的计算单元(SM),和真正的算子抢资源;V1 的 hook 机制支持通信等待阶段不持续占用 SM,但仍有发起/收尾成本、网络与显存竞争及依赖编排,重叠不是免费或自动发生的。

6. 核心技巧:非对称域带宽转发#

直接做法是:一个 token 命中的每个跨节点专家,都各发一份 RDMA——慢链路被打爆。

DeepEP 的做法(对应图中 dispatch 实线):

① 跨节点:一个 token 去某个目标节点,只走 1 次 RDMA,
   发到那个节点里的「一个」GPU;
② 节点内:再用快的 NVLink 把它转发到该节点内具体的专家 GPU。

也就是把「慢链路(RDMA)」的用量压到每 token 每目标节点最多一次,剩下的扩散都交给「快链路(NVLink)」。这与 V3 的 group-limited 路由是配套的:路由限制每 token 的目标节点数 ≤ M,转发量就有上界。

combine(图中虚线)是 dispatch 的逆过程:专家输出沿原路返回,最后按 gating 权重加权求和。


7. 其它工程点#

SM 数量可控    —— 可以指定通信占用多少 SM,方便和计算做重叠编排
FP8 / BF16     —— dispatch 支持低精度,combine 通常用高精度做加权求和
IBGDA          —— 低延迟路径基于 GPU 直发 RDMA,绕开 CPU 代理,降低延迟

8. 要点回顾#

DeepEP = MoE 专家并行的 dispatch/combine all-to-all 通信库。 高吞吐套件(训练/prefill)用 NVLink+RDMA + 非对称域带宽转发 + FP8;低延迟套件(decode)用纯 RDMA + 不占 SM 的 hook 重叠。它解决的是「token 在多卡多节点间高效散发与收回」这件事,是大规模 MoE 训练/推理的通信地基。

延伸阅读#

相关阅读路线

理解 MoE 的系统执行 ↗