DeepEP V1 vs V2 深度对比分析:统一 ElasticBuffer、路由缓存、Engram 与 SM-Free 通信
分析范围:DeepEP V1 (Legacy Buffer, NVSHMEM) vs V2 (ElasticBuffer, NCCL Gin) 的架构差异、路由缓存机制、Engram 远端内存访问、以及 Normal/Low-Latency 模式统一
核心源码文件:
- `deep_ep/buffers/legacy.py` (714 行) — V1 Legacy Buffer
- `deep_ep/buffers/elastic.py` (1108 行) — V2 ElasticBuffer
- `csrc/elastic/buffer.hpp` (1382 行) — ElasticBuffer C++ 核心
- `csrc/kernels/elastic/engram.hpp` — Engram JIT runtime
- `deep_ep/include/deep_ep/impls/engram_fetch.cuh` — Engram fetch kernel
- `csrc/kernels/legacy/internode_ll.cu` — V1 low-latency kernel
- `tests/elastic/test_ep.py` — V2 EP 测试(含 cached dispatch)
- `tests/elastic/test_engram.py` — Engram 测试
- `DeepEP/docs/legacy.md` — V1 官方文档
- `DeepEP/README.md` — V2 官方文档
目录
- [V1 vs V2 核心差异总览](#1-v1-vs-v2-核心差异总览)
- [统一 ElasticBuffer:V2 的核心设计哲学](#2-统一-elasticbufferv2-的核心设计哲学)
- [EPHandle 路由缓存机制](#3-ephandle-路由缓存机制)
- [为什么 Decode 用 Low-Latency 而 Prefill 用 Normal?](#4-为什么-decode-用-low-latency-而-prefill-用-normal)
- [SM-Free 通信:RDMA Hook 机制](#5-sm-free-通信rdma-hook-机制)
- [Normal vs Low-Latency 模式的算法差异](#6-normal-vs-low-latency-模式的算法差异)
- [Engram:远端内存访问原语](#7-engram远端内存访问原语)
- [DeepSeek MoE 路由与两种模式的配合](#8-deepseek-moe-路由与两种模式的配合)
- [关键引用与源码索引](#9-关键引用与源码索引)
- 两个 API 使用不同的 buffer 分配策略
- 不同的 kernel 文件 (
internode.cuvsinternode_ll.cu) - 不同的 handle 结构 (tuple of tensors)
- Low-Latency 模式无法与 Normal 模式共享 buffer
- 同一个 Buffer:所有通信模式共享
- 同一个 dispatch/combine API:参数控制行为
- SM 数量是主要区分因子(Normal 64-160 SMs, Low-Latency 4-8 SMs)
- 跳过 layout recomputation:
cached_num_recv_tokens,cached_psum_*直接传入 C++ kernel - 跳过 CPU sync:不需要等待 GPU→CPU 的信号
- 跳过 topk_idx clone:
do_handle_copy仍会执行 - C++ 层行为:
cached_mode = cached_num_recv_tokens.has_value()(buffer.hppL734) - Batch size 小(128 tokens)
- Routing decisions 在连续 decode step 之间可能不变(如果 gating 结果一致)
- 每次重新计算 layout 有 CPU overhead
- CPU sync 会引入 latency
- EP=16 internode: 43 GB/s, 4096 tokens
- EP=64 internode: 51 GB/s
- EP=8: 77 μs latency, 98 GB/s
- EP=64: 173 μs latency, 43 GB/s
- EP=256: 194 μs latency, 39 GB/s
return_recv_hook=True时,kernel 只发起 RDMA 请求,不等待完成- 返回的
hook是一个 callable,调用时阻塞等待数据到达 - RDMA 传输在网络硬件中进行,不占用 GPU SMs
- Attention(当前 batch)
- Dispatch RDMA(后台,SM-free)
- MoE GEMM(当前 batch)
- Combine RDMA(后台,SM-free)
- 0 SM Engram(RDMA one-sided get)
- 0 SM PP(RDMA send/recv)
- 0 SM CP(Copy Engine)
- 使用 NVLink 域内聚合 + RDMA 域间转发
- 适合 DeepSeek-V3 的 group-limited gating(top-4 groups, top-8 experts)
- 需要 24+ SMs 处理 forwarding 逻辑
- Buffer 模型复杂(5 层 buffer)
- 纯 RDMA(IBGDA — InfiniBand GPU Direct Async)
- GPU SM 直接操作 NIC QP,无需 CPU 介入
- 每个 expert 对应一个 QP(
num_qps_per_rank = num_local_experts) - 无 forwarding:直接 get 到目标位置
- 使用 2 个 buffer 交替(ping-pong)
- 需要 clean_low_latency_buffer 在 dirty 后重新 zero-init
- Remote KV Cache Fetching:
- 在 disaggregated inference 中,KV cache 可能存储在远程节点
- Engram 允许直接通过 RDMA 读取远端 KV cache entries
- 无需 CPU 介入,保持 GPU-centric
- Parameter Server 模式:
- 大模型参数存储在 CPU memory 或远程 GPU
- Engram 支持"拉取-计算"模式
engram_write将数据写入 CPU segment(cudaMemcpyDeviceToDevice)engram_fetch发起 Gin RDMA get 请求- 返回的 callable 在调用时等待完成
- 数据传输由 NIC hardware 完成,不占用 GPU SMs
- 256 experts 分为 32 groups(每 group 8 experts)
- 每个 token 选择 top-4 groups
- 在每个选中 group 内选择 top-2 experts
- 最终 top-8 experts per token
psum_num_recv_tokens_per_scaleup_rank:每个 scaleup rank 接收的 token 数前缀和psum_num_recv_tokens_per_expert:每个 expert 接收的 token 数前缀和recv_src_metadata:源 token 元数据dst_buffer_slot_idx:目标 buffer slot 索引DeepEP/README.md— V2 官方文档,统一 API 示例DeepEP/docs/legacy.md— V1 官方文档,Normal/Low-Latency 分离 APIDeepEP/docs/nvshmem.md— NVSHMEM 安装指南deep_ep/__init__.py— 包初始化,导出Buffer,ElasticBuffer,EPHandledeep_ep/buffers/legacy.py— V1 Buffer 类(714 行)deep_ep/buffers/elastic.py— V2 ElasticBuffer 类(1108 行)- L25-98: EPHandle 定义
- L100-193: deterministic_sort
- L195-368: ElasticBuffer 类定义
- L409-435: Engram storage size hint
- L569-604: engram_write/engram_fetch
- L728-834: get_theoretical_num_sms
- L855-1033: dispatch 方法
- L1046-1107: combine 方法
csrc/elastic/buffer.hpp— ElasticBuffer C++ 核心(1382 行)- L62-66: engram 状态
- L210-240: engram_write
- L242-325: engram_fetch
- L586-686: buffer size 计算
- L734: cached_mode 检测
csrc/kernels/elastic/dispatch.hpp— Dispatch JIT runtimecsrc/kernels/elastic/engram.hpp— Engram JIT runtimecsrc/kernels/backend/nccl.cu— NCCL Gin 后端deep_ep/include/deep_ep/impls/dispatch.cuh— Direct dispatch kerneldeep_ep/include/deep_ep/impls/hybrid_dispatch.cuh— Hybrid dispatch kerneldeep_ep/include/deep_ep/impls/engram_fetch.cuh— Engram fetch kerneldeep_ep/include/deep_ep/impls/combine.cuh— Combine kerneltests/elastic/test_ep.py— V2 EP 测试(含 cached dispatch)tests/elastic/test_engram.py— Engram 测试tests/legacy/test_low_latency.py— V1 low-latency 测试docs/10_deep_ep_v2_elastic_architecture.md— V2 架构全面分析docs/11_deep_ep_v2_deepdive_normal_lowlatency_sm_estimation.md— SM 估算与统一接口docs/08_test_barrier_engram_gate_analysis.md— Engram 测试分析docs/07_deep_ep_03_normal_vs_lowlatency.md— Normal vs Low-Latency 源码分析
1. V1 vs V2 核心差异总览
| 维度 | V1 (Legacy) | V2 (ElasticBuffer) |
|---|---|---|
| 通信后端 | NVSHMEM | NCCL Gin (GPU-Initiated Networking) |
| 编译方式 | 预编译 NVSHMEM kernel | Fully JIT (运行时编译) |
| API 设计 | Buffer 类,Normal/Low-Latency 两套独立 API |
ElasticBuffer 类,统一 API |
| Buffer 模型 | 分离的 NVL buffer + RDMA buffer | 统一 symmetric memory |
| SM 控制 | 静态变量 Buffer.num_sms = 24 |
解析式自动计算 get_theoretical_num_sms |
| QP 控制 | 手动指定 num_qps_per_rank |
解析式自动计算 get_theoretical_num_qps |
| 扩展规模 | EP ≤ 128 | EP ≤ 2048 |
| 新增功能 | 无 | Engram, PP, CP, AGRS |
| 性能 | 基准 | 1.3× 峰值带宽,4× SM 节省 |
来源: DeepEP/README.md L1-39, DeepEP/docs/legacy.md L1-9
2. 统一 ElasticBuffer:V2 的核心设计哲学
2.1 V1 时代的割裂
在 DeepEP V1 中,Normal 和 Low-Latency 是两套完全独立的实现:
# V1 Normal: 3-phase forwarding
num_tokens_per_rank, num_tokens_per_rdma_rank, num_tokens_per_expert, is_token_in_rank, event = \
buffer.get_dispatch_layout(topk_idx, num_experts)
recv_x, recv_topk_idx, recv_topk_weights, num_recv_tokens_per_expert_list, handle, event = \
buffer.dispatch(x, topk_idx=topk_idx, ...)
# V1 Low-Latency: 完全不同的 API
recv_hidden_states, recv_expert_count, handle, event, hook = \
buffer.low_latency_dispatch(x, topk_idx, num_max_dispatch_tokens_per_rank, num_experts)
关键问题:
22 V2 的统一设计
V2 的核心洞察:Normal 和 Low-Latency 的本质差异不是"通信协议",而是"资源分配策略"。
# V2 统一 API
recv_x, recv_topk_idx, recv_topk_weights, handle, event = buffer.dispatch(
x, topk_idx=topk_idx, topk_weights=topk_weights,
num_experts=num_experts,
num_sms=num_sms, # 控制 SM 数量:多=Normal, 少=Low-Latency
...
)
统一的维度:
来源: docs/11_deep_ep_v2_deepdive_normal_lowlatency_sm_estimation.md L22-60
3. EPHandle 路由缓存机制
3.1 EPHandle 的定义
EPHandle 是 dispatch 返回的路由元数据对象,包含所有用于后续 combine 的信息:
class EPHandle:
do_expand: bool
num_experts: int
expert_alignment: int
num_max_tokens_per_rank: int
num_sms: int # dispatch 时使用的 SM 数, combine 时复用
topk_idx: Tensor # [num_tokens, num_topk], clone 的 expert indices
psum_num_recv_tokens_per_scaleup_rank: Tensor # [num_scaleup_ranks]
psum_num_recv_tokens_per_expert: Tensor # [num_local_experts]
num_unaligned_recv_tokens_per_expert: Tensor # [num_local_experts]
num_recv_tokens_per_expert_list: list # CPU-side per-expert counts
recv_src_metadata: Tensor # 源 token 元数据
dst_buffer_slot_idx: Tensor # 目标 buffer slot 索引
token_metadata_at_forward: Optional[Tensor] # Hybrid 模式
channel_linked_list: Optional[Tensor] # Hybrid 模式
来源: deep_ep/buffers/elastic.py L25-98
3.2 Cached Dispatch 机制
当 handle 参数被传入 dispatch 时,进入 cached mode:
# elastic.py L937-944
if handle is not None:
assert topk_idx is None # 不再接受新的 topk_idx
assert do_cpu_sync is None or not do_cpu_sync # 禁止 CPU sync
topk_idx = handle.topk_idx # 复用缓存的 topk_idx
num_max_tokens_per_rank = value_or(num_max_tokens_per_rank, handle.num_max_tokens_per_rank)
num_experts = value_or(num_experts, handle.num_experts)
do_cpu_sync = False # 关键:跳过 CPU sync
Cached mode 的关键行为:
3.3 为什么需要路由缓存?
Decode 场景的特殊性:
使用方式(README.md L271-329):
def decode_dispatch(x, topk_idx, topk_weights, ...):
if cached_handle is not None:
# Reuse cached handle: skip layout recomputation and CPU sync
recv_x, _, _, handle, event = _buffer.dispatch(
x, handle=cached_handle, ...)
return recv_x, cached_handle.topk_idx, None, handle, event
# 首次:完整计算
recv_x, recv_topk_idx, recv_topk_weights, handle, event = _buffer.dispatch(...)
return recv_x, recv_topk_idx, recv_topk_weights, handle, event
来源: DeepEP/README.md L271-329, deep_ep/buffers/elastic.py L937-944
4. 为什么 Decode 用 Low-Latency 而 Prefill 用 Normal?
4.1 工作负载特征对比
| 维度 | Prefill (Normal) | Decode (Low-Latency) |
|---|---|---|
| Batch size | 4096-8192 tokens | 64-256 tokens |
| 优化目标 | 吞吐量 (GB/s) | 延迟 (μs) |
| 并行度 | 高(大量数据可并行) | 低(少量数据) |
| Latency hiding | 容易(大计算量) | 困难(小计算量) |
| 通信模式 | 3-phase forwarding | Direct RDMA get |
4.2 官方文档的说明
Normal kernels(docs/legacy.md L9-10):
"These kernels deliver high throughput, making them suitable for both training and inference prefilling tasks."
Low-latency kernels(docs/legacy.md L11):
"For latency-sensitive inference decoding, DeepEP V1 includes a set of low-latency kernels with pure RDMA to minimize delays."
4.3 量化分析
V1 Normal 性能(docs/legacy.md L17-26):
V1 Low-Latency 性能(docs/legacy.md L29-39):
关键洞察:Low-Latency 在小 EP 时延迟极低(77μs),但随 EP 增大延迟上升。Normal 的吞吐量稳定在 43-58 GB/s。
4.4 V2 的统一解释
在 V2 中,"Low-Latency" 不是独立模式,而是 Normal 在特定参数配置下的特例:
| 维度 | Normal 场景 | Low-Latency 场景 |
|---|---|---|
| SM 数 | 多(64-160) | 少(4-8) |
| QP 数 | 多(SM×16+1) | 少(SM+1) |
| prefer_overlap | False | True |
| Buffer | 相同 | 相同 |
来源: docs/11_deep_ep_v2_deepdive_normal_lowlatency_sm_estimation.md L136-150, DeepEP/docs/legacy.md L9-11
5. SM-Free 通信:RDMA Hook 机制
5.1 V1 的 Hook-Based Overlap
V1 Low-Latency 模式最引人注目的特性:0 SM occupation 通信。
# docs/legacy.md L264-272
recv_hidden_states, recv_expert_count, handle, event, hook = \
buffer.low_latency_dispatch(x, topk_idx, ...,
return_recv_hook=True)
# NOTES: the actual tensor will not be received only if you call `hook()`,
# it is useful for double-batch overlapping, but **without any SM occupation**
机制:
5.2 双 Batch Overlap
docs/legacy.md L288-290 描述的 overlap 模式:
Batch 1: [Attention] → [Dispatch] → [MoE Compute] → [Combine]
Batch 2: [Attention] → [Dispatch] → [MoE Compute] → [Combine]
↑ 此时 Batch 1 的 RDMA 传输在后台进行,不占 SM
4 个阶段的 overlap:
5.3 V2 的变化
重要: V2 README 明确说明:
"**Notes**: ... 0 SM RDMA low-latency EP is no longer supported"
V2 不再支持纯 0 SM 的 low-latency EP,但保留了:
V2 的通信-computation overlap 通过 EventOverlap + async_with_compute_stream=True 实现,仍然占用少量 SM。
来源: DeepEP/README.md L28-29, DeepEP/docs/legacy.md L11, L264-290
6. Normal vs Low-Latency 模式的算法差异
6.1 Normal 模式:3-Phase Forwarding
Phase 1: Push (NVLink)
Local Rank → Send Buffer (via NVLink)
Phase 2: Forward (RDMA)
Scaleup Warps → Scaleout RDMA → Remote Node
Phase 3: Reduce (Local)
Received tokens → Expert computation
特点:
6.2 Low-Latency 模式:Direct RDMA Get
Direct: Local Token → RDMA Get → Remote Expert Buffer
特点:
6.3 Buffer 布局差异
V1 Normal Buffer:
[NVL Send Buffer] [NVL Recv Buffer] [RDMA Send Buffer] [RDMA Recv Buffer] [Expert Buffer]
V1 Low-Latency Buffer:
[RDMA Buffer 0] [RDMA Buffer 1] # Double buffering
形状: [num_local_experts, num_ranks * num_max_dispatch_tokens_per_rank, hidden]
V2 Unified Buffer:
[Workspace] [GPU Buffer] [CPU Buffer (for Engram)]
6.4 Kernel 对比
| 维度 | Normal (V1) | Low-Latency (V1) | V2 Unified |
|---|---|---|---|
| Kernel 文件 | internode.cu |
internode_ll.cu |
dispatch.cuh / hybrid_dispatch.cuh |
| SM 角色 | Dispatch + Notify + Forward | Dispatch only | Dispatch + Notify (configurable) |
| 同步 | 多阶段 barrier | 简单 barrier | Gin barrier |
| 数据布局 | 5 层 buffer | 2 层 buffer | 统一 token layout |
来源: DeepEP/docs/legacy.md L9-11, L528-550, docs/11_deep_ep_v2_deepdive_normal_lowlatency_sm_estimation.md L22-60
7. Engram:远端内存访问原语
7.1 Engram 的定义
Engram 是 DeepEP 弹性模式的远端内存访问原语——通过 NCCL Gin(GPU-initiated RDMA)直接读取远端 GPU 的存储窗口(ncclWindow),无需 CPU 介入。
| API | 语义 |
|---|---|
engram_write(storage, sf) |
将本地数据注册到 NCCL window,供远端读取 |
engram_fetch(indices)() |
异步发起 RDMA get,返回 callable hook;调用 hook() 阻塞等待完成 |
来源: docs/08_test_barrier_engram_gate_analysis.md L87-93
7.2 Engram 与 KV Cache 的关系
Engram 的设计目标是跨节点参数/激活拉取,典型用例包括:
存储布局(elastic.py L409-435):
@staticmethod
def get_engram_storage_size_hint(num_entries, hidden, num_max_tokens_per_rank, dtype):
# GPU buffer: 用于接收 fetched 数据
num_gpu_bytes = align(hidden * dtype.itemsize * num_max_tokens_per_rank, 2MB)
# CPU buffer: 用于本地 storage(远端可读取)
num_cpu_bytes = align(hidden * dtype.itemsize * num_entries, 2MB)
return num_gpu_bytes, num_cpu_bytes
内存布局(buffer.hpp L229-236):
Hybrid Elastic Symmetric Memory:
[ GPU VRAM | CPU rank0 | CPU rank1 | ... | CPU rank(N-1) ]
↑ Engram storage 写入这里
7.3 Engram 的 0 SM 特性
Engram 是 V2 中少数保留的 0 SM 功能:
// buffer.hpp L292-309
launch_engram_fetch(
nccl_context->dev_comm, nccl_context->window,
math::advance_ptr(buffer, num_gpu_buffer_bytes), // CPU segment
fetched.data_ptr(),
indices.data_ptr<int>(),
...
);
机制:
7.4 Engram 的 FP8 支持
# elastic.py L579-602
def engram_write(self, storage, sf=None):
"""
storage: [num_entries, hidden], torch.bfloat16 or torch.float8_e4m3fn
sf: [num_total_entries, num_sf_packs], FP8 scaling factors
"""
关键:FP8 scaling factors 是全局冗余的(每个 rank 持有全部 SF),只有 main data 通过 RDMA 传输。
来源: csrc/kernels/elastic/engram.hpp, csrc/elastic/buffer.hpp L210-325, tests/elastic/test_engram.py
8. DeepSeek MoE 路由与两种模式的配合
8.1 DeepSeek-V3 的 Group-Limited Gating
DeepSeek-V3 采用 group-limited gating 策略:
意义:限制每个 token 只能访问 4 个 group → 减少跨节点通信量。
8.2 Normal 模式的优化
DeepEP V1 Normal 内核针对 group-limited gating 做了专门优化:
"To align with the group-limited gating algorithm proposed in the DeepSeek-V3 paper, DeepEP V1 offers a set of kernels optimized for asymmetric-domain bandwidth forwarding, such as forwarding data from NVLink domain to RDMA domain."
docs/legacy.md L9
8.3 Decode 场景的路由缓存
问题:在 decode 阶段,如果连续 batch 的 routing decisions 相同,每次重新计算 layout 浪费。
V2 解决方案:EPHandle 缓存
# 首次 decode step
recv_x, recv_topk_idx, recv_topk_weights, handle, event = buffer.dispatch(
x, topk_idx=topk_idx, topk_weights=topk_weights, ...)
# 后续 step(routing 不变)
recv_x, _, _, handle, event = buffer.dispatch(x, handle=handle)
缓存的内容:
8.4 Deterministic Sort
DeepEP 支持确定性路由(deterministic=True),通过 EPHandle.deterministic_sort() 对接收到的 token 排序,确保跨 rank 的一致性。
# elastic.py L100-193
def deterministic_sort(self, do_cpu_sync, is_cached_dispatch,
recv_x, recv_sf, recv_topk_idx, recv_topk_weights,
channel_linked_list):
# Non-expand mode: sort recv_x, recv_sf, recv_topk_weights, recv_topk_idx
# Expand mode: only sort expanded arrays
来源: DeepEP/docs/legacy.md L9, deep_ep/buffers/elastic.py L100-193, DeepEP/README.md L271-329