DeepEP#
本页内容9 个部分
查看概览图
把慢链路的用量
压到每节点一次
两个 all-to-all(dispatch / combine)是大规模 MoE 的主要通信瓶颈。此图讲解 DeepEP V1 的 normal / low-latency 两条路径;V2 的通信后端和接口已有变化。
每层每 token 都要搬运 hidden——prefill / 训练时 token 海量。
decode 阶段每步 token 很少,all-to-all 的固定延迟被放大。
节点内 NVLink 快(~150GB/s 级),跨节点 RDMA / IB 慢得多。
跨节点:一个 token 去某个目标节点只走 1 次 RDMA,发到那个节点里的「一个」GPU;节点内:再用快的 NVLink 转发到具体的专家 GPU。把慢链路的用量压到每 token 每目标节点最多一次,剩下的扩散都交给快链路。
高吞吐 · 训练 / 推理 prefill
-
双链路
NVLink + RDMA 同时用 -
非对称域带宽转发见上 -
配合 group-limited 路由每 token 最多发到 ≤ M 个节点,跨节点流量可控 -
低精度
FP8 dispatch通信字节直接减半
低延迟 · 推理 decode
-
单链路
纯 RDMA为最小化延迟而设计 -
hook 式通信-计算重叠RDMA 异步发起,hook 在后续计算时收尾 -
关键
V1 低延迟路径支持不占用 SM 的重叠仍占用网络、显存带宽等资源;V2 已改变通信实现
combine 是 dispatch 的逆过程:专家输出沿原路返回,最后按 gating 权重加权求和。group-limited 路由限制每 token 的目标节点数 ≤ M,转发量因此有上界。
版本范围:本文主要解释 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 训练/推理的通信地基。
延伸阅读#
- DeepSeek-V4:路由评分与残差连接:路由的选择分数、混合权重与残差连接分别承担什么作用?
- MLA:潜在表示压缩与解耦 RoPE:如何用压缩表示减少 KV Cache,并处理 RoPE 与矩阵吸收的冲突?