三视角综合:架构 · 代码 · 理论 | 2026-07-27
基于 DeepEP 源码逐行分析 + 互联网调研 + 交叉讨论修正
DeepEP(DeepEveryParallel)是 DeepSeek 团队开发的 高性能 Expert Parallelism 通信库,核心目标是在 MoE(Mixture-of-Experts)模型训练和推理中实现 高吞吐、低延迟的 all-to-all GPU 通信。V2 版本相比 V1 实现了 1.3x 峰值性能提升,同时节省最多 4x SM 资源占用(从 24 SM 降至 4-6 SM)。
通过架构、代码、理论三个独立视角的交叉验证,我们提炼出以下核心设计原理:
MoE 模型的核心是 条件计算(Conditional Computation):每个 token 通过 Gate 网络独立选择 Top-K 个 expert。设 token 数为 $T$,expert 数为 $E$,top-k 为 $K$,则 routing 矩阵 $R \in \{0,1\}^{T \times E}$ 的熵为:
H(R) = T \cdot K \cdot \log_2(E/K) \quad \text{bits}
对于 DeepSeek-V3 配置(T=8192, E=256, K=8),每次 forward pass 的 routing 熵约为 2.6 MB 的不可压缩随机信息。这意味着:
在 EP(Expert Parallelism)中,每个 token 可能被路由到任意 rank,导致通信量放大:
通信量 = T \times K \times hidden\_size \times dtype\_size 对于 V3 配置(8K tokens, top-8, 7168 hidden, BF16): = 8192 \times 8 \times 7168 \times 2 = 939.5 MB per layer
这解释了为什么 EP 通信是 MoE 训练的瓶颈——通信量与 top-k 成正比,且每个 token 的目标 rank 不可预测。
| 互联类型 | 带宽 (GB/s) | 延迟 | 设计含义 |
|---|---|---|---|
| NVLink (SM100) | ~726 | ~1 μs | scaleup 域内全带宽通信 |
| RDMA (CX7) | ~90 | ~5 μs | scaleout 域间带宽受限 |
| NVLink/RDMA 比 | ~8x | ~5x | 层次化通信的动机 |
DeepEP 的 Hybrid mode 正是利用这一差距:scaleup 域内使用 NVLink 全带宽,scaleout 域间使用 RDMA,通过层次化 all-to-all 最小化跨域流量。
这是三个 agent 从不同角度共同证明的核心原理:
与 Dispatch 的随机访存不同,Combine 阶段可以顺序遍历接收到的 token:
// combine.cuh L86-93: 顺序遍历 token
for (int i = token_start_idx; i < token_end_idx; ++ i) {
const int src_token_idx = __ldg(src_metadata + i * kMetadataStride) % kNumMaxTokensPerRank;
const int src_rank_topk_idx = __ldg(src_metadata + i * kMetadataStride + 1);
...
}
这是因为 dispatch 阶段已将 routing 信息保存在 recv_src_metadata 中,combine 只需逆序执行即可。
DeepEP V2 使用 NCCL Gin backend 实现 GPU 直接发起 RDMA 操作:
// dispatch.cuh L388-391: RDMA put 由 GPU warp 直接发起
gin.put<team_t>(recv_buffer.get_token_buffer(stored_dst_slot_idx).get_base_ptr(),
send_buffer_ptr, tma_buffer.get_num_bytes<false>(), stored_dst_rank_idx);
这消除了传统 RDMA 需要的 CPU 代理开销,实现了 kernel-level all-to-all。
| 维度 | V1 (Legacy) | V2 (Elastic) | 改进 |
|---|---|---|---|
| 后端 | NVSHMEM | NCCL Gin | 更轻量,复用 NCCL communicator |
| 编译 | 预编译 | JIT (Just-In-Time) | 运行时编译,无需安装 CUDA |
| API | 分离的 dispatch/combine | 统一 ElasticBuffer | 简化接口,支持 handle 缓存 |
| SM 占用 | 24 SM | 4-6 SM | 4x 节省 |
| 扩展性 | EP256 | EP2048 | 8x 扩展 |
| Auto-tuning | 需要 | Analytical model | 无需调优 |
ElasticBuffer 是 V2 的核心抽象,统一了:
DeepEP 将 EP 域分为两层:
Hybrid mode 利用这一分层,在 scaleup 域内做 reduction,减少 scaleout 通信量。
| 模式 | 适用场景 | 通信路径 | 带宽 |
|---|---|---|---|
| Direct | 单节点(EP ≤ 8) | 纯 NVLink | 最高 |
| Hybrid | 多节点(EP > 8) | NVLink + RDMA 层次化 | 较高 |
Dispatch kernel(dispatch.cuh)是 DeepEP 最复杂的组件,分为三个阶段:
// dispatch.cuh L79-107: Notify warps 统计每个 expert 和 rank 的 token 数量
for (int i = global_warp_idx; i < num_tokens; i += kNumNotifyWarps * kNumSMs) {
const auto dst_expert_idx = lane_idx < kNumTopk ?
static_cast<int>(__ldg(topk_idx + i * kNumTopk + lane_idx)) : -1;
if (dst_expert_idx >= 0)
atomicAdd_block(expert_count + dst_expert_idx, 1);
// Rank choice with deduplication
const auto dst_rank_idx = dst_expert_idx >= 0 ? dst_expert_idx / kNumExpertsPerRank : -1;
if (ptx::deduplicate(dst_rank_idx, lane_idx) and dst_rank_idx >= 0)
atomicAdd_block(rank_count + dst_rank_idx, 1);
}
Notify warps 使用 shared memory atomic 进行本地计数,然后通过 ptx::red_add 进行全局 reduction,最后通过 NCCL Gin 将 count 写入对端。
// dispatch.cuh L287-351: TMA 加载 token 数据 + atomic slot 分配
ptx::tma_load_1d(tma_buffer.get_hidden_ptr(), ..., kNumHiddenBytes);
// ...
stored_dst_slot_idx = atomicAdd(workspace_layout.get_scaleup_atomic_sender_counter()
+ stored_dst_rank_idx, 1);
每个 dispatch warp 独立处理 token,使用 TMA 从全局内存加载 hidden 数据到 shared memory,然后通过 atomicAdd 分配目标 slot。
// dispatch.cuh L372-393: NVLink TMA store 或 RDMA put
if (dst_ptr != nullptr)
ptx::tma_store_1d(dst_ptr, tma_buffer.get_base_ptr(), ...); // NVLink
else
gin.put<team_t>(recv_buffer..., send_buffer_ptr, ..., stored_dst_rank_idx); // RDMA
根据目标 rank 是否 NVLink 可达,选择 TMA store(零拷贝)或 RDMA put(通过 send buffer)。
TokenLayout 定义了单个 token 在通信 buffer 中的布局:
// layout.cuh L179-248: TokenLayout 结构
struct TokenLayout {
int num_hidden_bytes; // hidden_size * dtype_size (e.g., 7168*2=14336)
int num_sf_bytes; // FP8 scale factor bytes
int num_topk; // top-k indices
int num_metadata_bytes; // topk_idx + weights + src_token_global_idx
// 内存布局 (TMA aligned):
// | hidden (aligned) | SF (aligned) | metadata (aligned) | mbarrier (if with) |
};
关键设计:所有段都 TMA 对齐(128 bytes),确保 TMA load/store 的正确性。
EPHandle 是 dispatch 返回的 routing 元数据,包含 combine 所需的全部信息:
| 字段 | 类型 | 数学含义 |
|---|---|---|
| psum_num_recv_tokens_per_scaleup_rank | int[num_scaleup_ranks] | 每个 scaleup rank 的接收 token 前缀和 |
| psum_num_recv_tokens_per_expert | int[num_local_experts] | 每个 local expert 的 token 偏移(exclusive prefix sum) |
| recv_src_metadata | int[num_recv][2+topk] | 源 token 全局索引 + rank/topk 索引 + 展开目标索引 |
| num_recv_tokens_per_expert_list | list | 每个 expert 接收的 token 数(CPU 可读) |
FP8 加法不满足结合律,导致不同到达顺序的 token 累加结果不同。DeepEP 通过 deterministic sort 保证训练可复现:
// elastic.py L100-163: deterministic_sort // 非 expand 模式:按 src_token_global_idx 排序所有接收到的 tensor // expand 模式:按 (expert_idx, src_token_global_idx) 排序 orig_indices = torch.sort(sort_keys).indices permute(recv_x, orig_indices) permute(recv_topk_weights, orig_indices)
DeepEP V2 使用 analytical model 计算最优 SM 数量,无需 auto-tuning:
// elastic.py L728-834: get_theoretical_num_sms
# 计算期望 top-k(balanced gate)
num_expected_topk = num_groups * (1 - C(E-E/g, K) / C(E, K))
# 计算各维度流量
sm_read = 1 / num_expected_topk
sm_write = ...
rdma_traffic = ...
nvlink_traffic = ...
# 找到瓶颈
bounded_traffic = max(rdma_traffic, nvlink_traffic)
bounded_gbs = corresponding bandwidth
# SM 数量 = 瓶颈带宽需求 / 每 SM 带宽能力
num_sms = max(bounded_gbs/traffic * sm_read/sm_read_gbs,
bounded_gbs/traffic * sm_write/sm_write_gbs)
num_sms = align(max(4, ceil(num_sms * 1.25)), 2)
这是三个 agent 从不同角度共同回答的核心问题。综合三视角,根本原因如下:
下游 expert GEMM 要求同一 expert 的 token 在内存中连续排列。但 dispatch 完成后,token 按到达顺序排列(来自不同 rank 的不同 token 交错),而非按 expert ID 排序。这是一个 permutation 操作,无法避免。
为实现高效的 GEMM(如 128 对齐),每个 expert 的 token 数需要对齐到 expert_alignment。这进一步要求重组时插入 padding。
FP8 加法不满足结合律,不同累加顺序产生不同结果。为保证训练可复现,必须按确定性顺序排列 token。
设 token $t$ 选择 expert $e$ 的概率为 $p(e|t) = \text{softmax}(W_g \cdot x_t)$。对于不同的 token $t_1 \neq t_2$,其 routing 决策是条件独立的:
p(e_1 | t_1) ⊥ p(e_2 | t_2) for t_1 ≠ t_2
因此,token $t$ 的目标 rank $r(t) = e(t) / E_{per\_rank}$ 是一个随机变量,其分布取决于 Gate 网络的输出。对于 balanced gate:
P(r(t) = r) = 1 / num_ranks, ∀ r
这意味着:
既然随机访存不可消除,DeepEP 通过以下机制隐藏随机访存的延迟:
| 机制 | 作用 | 代码位置 |
|---|---|---|
| TMA 硬件卸载 | 异步数据传输,不占用 SM 计算资源 | dispatch.cuh L287-291 |
| Warp 特化 | notify warps 和 dispatch warps 并行执行 | dispatch.cuh L79-395 |
| mbarrier 同步 | fine-grained producer-consumer 同步 | dispatch.cuh L271-359 |
| QP 并发 | 多个 Queue Pair 并行发送 RDMA 请求 | comm.cuh L56-86 |
| Atomic slot allocation | 无锁并行写入,每个 warp 独立分配 | dispatch.cuh L344-350 |
Combine 是 Dispatch 的逆操作,但访问模式完全不同:
Combine 能顺序读取是因为 dispatch 阶段已将 routing 信息保存在 recv_src_metadata 中。但 Agent 2 指出:combine 的写入仍然是随机的(目标 rank 不可预测),只是读取可以是顺序的。
| Arch | NIC | Topo | Dispatch BW | Combine BW | #SMs |
|---|---|---|---|---|---|
| SM90 | CX7 | EP 8×2 | 90 GB/s | 81 GB/s | 12 |
| SM90 | CX7 | EP 8×4 | 61 GB/s | 61 GB/s | 6 |
| SM100 | CX7 | EP 8×2 | 90 GB/s | 91 GB/s | 12 |
| SM100 | N/A | EP 8 | 726 GB/s | 740 GB/s | 64 (Max) |
| SM100 | N/A | EP 8 | 643 GB/s | 675 GB/s | 24 (Min) |
数据来源:DeepEP README.md 官方 benchmark
| 方案 | Backend | SM 占用 | 扩展性 | 特点 |
|---|---|---|---|---|
| DeepEP V2 | NCCL Gin | 4-6 | EP2048 | JIT, 层次化, FP8 |
| DeepEP V1 | NVSHMEM | 24 | EP256 | 预编译, auto-tuning |
| Megatron-LM | NCCL | 高 | 有限 | 通用但非专用 |
| DeepSpeed-MoE | NCCL | 中 | 中 | 与 DeepSpeed 集成 |
| Tutel | NCCL | 中 | 中 | 微软, 自适应 |
三个 agent 在完成独立报告后,互相审阅了对方的工作并进行了交叉讨论。以下是讨论的核心内容:
| Agent | 从 Agent 1 学到 | 从 Agent 2 学到 | 从 Agent 3 学到 |
|---|---|---|---|
| Agent 1 | — | TMA 硬件卸载是 0 SM 的基础; Warp 特化 | Routing 熵的信息论下界; 时间反转对称性 |
| Agent 2 | SM 最小化的架构意义 | — | Metadata 信息论下界; FP8 非结合律论证 |
| Agent 3 | Analytical SM Count Model | mbarrier Phase 翻转; QP 分配两种模式 | — |
关于"为什么要重组":本质是从高熵布局到低熵布局的转换,根植于 MoE 条件计算的数学本质。GEMM 要求连续内存、expert alignment 要求 padding、FP8 要求确定性顺序——三重约束共同决定了重组不可避免。
关于"为什么一定是随机访存":不是设计选择而是数学必然。MoE 的核心价值(条件计算)要求每个 token 独立选择 expert,预取准确率仅 ~0.4%。DeepEP 的设计哲学是"承认不可预测性,利用层次化带宽,逼近理论极限"——通过 TMA 硬件卸载 + warp 特化 + QP 并发将随机访存的延迟隐藏到接近零。
| 文件 | 行数 | 核心内容 |
|---|---|---|
| deep_ep/impls/dispatch.cuh | 411 | Dispatch kernel 完整实现 |
| deep_ep/impls/combine.cuh | 245 | Combine kernel 完整实现 |
| deep_ep/impls/dispatch_copy_epilogue.cuh | 325 | Copy epilogue(重组核心) |
| deep_ep/impls/combine_reduce_epilogue.cuh | 145 | Reduce epilogue |
| deep_ep/impls/hybrid_dispatch.cuh | ~400 | Hybrid mode dispatch |
| deep_ep/impls/hybrid_combine.cuh | ~350 | Hybrid mode combine |
| deep_ep/common/layout.cuh | 313 | TokenLayout/BufferLayout/WorkspaceLayout |
| deep_ep/common/comm.cuh | ~200 | NCCL Gin 通信原语 |
| deep_ep/common/ptx.cuh | ~200 | PTX 内联汇编工具 |
| deep_ep/buffers/elastic.py | 1108 | Python 接口层 + EPHandle |
| deep_ep/utils/refs.py | ~200 | 参考实现(NCCL 版) |
| deep_ep/utils/gate.py | 181 | 测试数据生成 |
| 功能 | 文件 | 行号 |
|---|---|---|
| Notify warps expert counting | dispatch.cuh | 93-107 |
| Atomic slot allocation | dispatch.cuh | 344-350 |
| TMA load hidden data | dispatch.cuh | 287-291 |
| TMA store NVLink | dispatch.cuh | 372-378 |
| RDMA put | dispatch.cuh | 388-391 |
| Combine 顺序遍历 | combine.cuh | 86-93 |
| Expand mode atomic | dispatch_copy_epilogue.cuh | 120-122 |
| Deterministic sort | elastic.py | 100-163 |
| SM analytical model | elastic.py | 728-834 |
| TokenLayout 定义 | layout.cuh | 179-248 |
| BufferLayout 定义 | layout.cuh | 251-311 |
| 文件 | 大小 | 视角 | 核心贡献 |
|---|---|---|---|
| agent1_report.html | 35 KB | 架构 | V1→V2 演进、ElasticBuffer 抽象、SM 模型 |
| agent2_report.html | 75 KB | 代码 | TMA/mbarrier/QP 机制、warp 特化、内存布局 |
| agent3_report.html | 43 KB | 理论 | Routing 熵、信息论下界、DeepSeek-V3 关联 |
| discussion_agent1_response.html | 20 KB | 架构交叉 | 硬件约束决定性、Expand mode 工程视角 |
| discussion_agent2_response.html | 18 KB | 代码交叉 | "读顺序写随机"精确表述、TMA 应对核心 |
| discussion_agent3_response.html | 16 KB | 理论交叉 | 信息论框架、统一设计哲学 |
| 术语 | 含义 |
|---|---|
| EP | Expert Parallelism,专家并行 |
| MoE | Mixture-of-Experts,专家混合模型 |
| TMA | Tensor Memory Accelerator,Hopper 架构硬件 |
| mbarrier | Hopper 架构的 memory barrier 同步原语 |
| Gin | NCCL 的 GPU-initiated networking backend |
| QP | Queue Pair,RDMA 通信队列对 |
| SM | Streaming Multiprocessor,GPU 流多处理器 |
| RDMA | Remote Direct Memory Access |
| NVLink | NVIDIA GPU 间高速互联 |
| Scaleup | NVLink 域内(通常同节点) |
| Scaleout | RDMA 域间(跨节点) |
| Expand Mode | 每个 token 为每个 top-k expert 分配独立 slot |
| Hybrid Mode | 层次化 scaleup+scaleout 通信 |