DeepEP 5-Layer Buffer ↔ DeepEP 源码 ↔ DeepGEMM SymmBuffer 三方映射分析
分析日期: 2026-07-30
目标: 将博客描述的 5-Layer Buffer 模型精确映射到 DeepEP 实际源码和 DeepGEMM Mega MoE 实现
关联文档: 05_02_buffer_symmbuffer.md
1. Blog 原文:5-Layer Buffer 模型
博客第 2 节 "DeepEP Buffer System: Data Flow Organization Under Different Kernels" 原文描述:
2.1 Normal Kernel: Throughput-Optimized Complete Pipeline
Data path:
Token Buffer → Dispatch Buffer → Chunk Buffer → NVLink / RDMA Pipeline → Receive Buffer → Expert Buffer
逐层原文定义:
- Token Buffer: "Stores Router output. Layout: Token-major."
- Dispatch Buffer: "First layout transformation: Token-major → Destination-major."
- Chunk Buffer: "Critically important. The network is unsuitable for single-Token sends — produces small packets, high startup overhead, low bandwidth utilization. Tokens are aggregated:
Token Stream → Chunk → Network Transfer. Token is the scheduling granularity; Chunk is the communication granularity." - Receive Buffer: "Destination GPU receives Chunks from multiple GPUs."
- Expert Buffer: "Final transformation: Destination-major → Expert-major, forming Expert GEMM input."
2.2 Low-Latency Kernel: Decode-Oriented Short Path
Decode: small batch, few Tokens, single-Token latency sensitive. Waiting for Chunk aggregation increases latency.
Low-Latency Kernel reduces intermediate layers:
Token Buffer → Direct RDMA → Receive Buffer → Expert Buffer
Goal: minimize end-to-end Token latency.
2.3 核心概念:Token vs Chunk 双粒度
| 粒度 | 角色 | 操作 |
|---|---|---|
| Token | scheduling granularity | Router 输出、GEMM 计算 |
| Chunk | communication granularity | 网络传输(NVLink/RDMA) |
2. DeepEP 源码实现:5-Layer Buffer 的精确映射
2.1 Legacy Buffer 系统(最贴近博客描述)
Legacy 系统(csrc/kernels/legacy/)是博客描述的最直接对应。从 internode.cu 可以看到完整的 buffer 分配:
// csrc/kernels/legacy/internode.cu: 526-530
// RDMA symmetric layout: 每个 channel 有 send/recv 两套 buffer
auto rdma_channel_data = SymBuffer<uint8_t>(
rdma_buffer_ptr, num_max_rdma_chunked_recv_tokens * num_bytes_per_token,
kNumRDMARanks, channel_id, num_channels);
auto rdma_channel_meta = SymBuffer<int>(
rdma_buffer_ptr, LEGACY_NUM_MAX_NVL_PEERS * 2 + 2,
kNumRDMARanks, channel_id, num_channels);
// csrc/kernels/legacy/internode.cu: 545-560
// NVL buffer layouts: 每个 channel 也有 send/recv
auto nvl_channel_x = AsymBuffer<uint8_t>(ws_rr_buffer_ptr,
num_max_nvl_chunked_recv_tokens * num_bytes_per_token,
LEGACY_NUM_MAX_NVL_PEERS, channel_id, num_channels, rs_wr_rank)
.advance_also(rs_wr_buffer_ptr);
Legacy 的 5-Layer 对应关系
| Blog Buffer 层 | Legacy 源码对应 | 代码位置 |
|---|---|---|
| Token Buffer | x (输入 tensor, [num_tokens, hidden]) |
internode.cu:458 |
| Dispatch Buffer | rdma_channel_data.send_buffer(rdma_rank) / nvl_channel_x |
internode.cu:631, 545 |
| Chunk Buffer | num_max_rdma_chunked_send_tokens 控制的 FIFO 窗口 |
internode.cu:480, 527 |
| Receive Buffer | rdma_channel_data.recv_buffer(rdma_rank) / nvl_channel_x (recv 侧) |
internode.cu:527, 545 |
| Expert Buffer | recv_x (输出 tensor, 按 expert 重排) |
internode.cu:453 |
Chunk 机制的核心代码
// csrc/kernels/legacy/internode.cu: 647-648
// Wait the remote buffer to be released
while (is_token_in_rank_uint64 != 0 and
rdma_tail_idx - cached_rdma_channel_head >= num_max_rdma_chunked_recv_tokens) {
// 等待 chunk 窗口释放
}
关键洞察: Legacy 的 Chunk 是一个 FIFO 滑动窗口(head/tail 控制),Token 累积到一定数量后作为一个 Chunk 发送。num_max_rdma_chunked_recv_tokens 定义了接收端 Chunk 缓冲区容量。
2.2 Elastic Buffer 系统(当前主力)
Elastic 系统(deep_ep/buffers/elastic.py + csrc/kernels/elastic/)是 DeepEP 的演进版本,核心变化是用 NCCL Symmetric Window 替代 NVSHMEM,用 TMA 替代手动 copy。
Buffer 布局定义
// deep_ep/include/deep_ep/common/layout.cuh: 251-311
template <bool kWithMBarrier>
struct BufferLayout {
TokenLayout token_layout;
int num_ranks;
int num_max_tokens_per_rank;
void* base;
// 每个 rank 的 buffer 大小
int64_t get_num_bytes_per_rank() const {
return num_max_tokens_per_rank * get_num_bytes_per_token();
}
// 总 buffer 大小 = per_rank * num_ranks
int64_t get_num_bytes() const {
return get_num_bytes_per_rank() * num_ranks;
}
};
Dispatch Kernel 中的 Buffer 使用
// deep_ep/include/deep_ep/impls/dispatch.cuh: 263-268
// Buffer layouts
const auto token_layout = layout::TokenLayout(kNumHiddenBytes, ...);
auto tma_buffer = layout::BufferLayout<true>(token_layout, kNumDispatchWarps, 1, ...);
auto recv_buffer = layout::BufferLayout<false>(token_layout, kNumRanks, kNumMaxTokensPerRank, buffer);
auto send_buffer = layout::BufferLayout<false>(token_layout, 1, kNumMaxTokensPerRank,
recv_buffer.get_buffer_end_ptr());
Elastic 的内存布局:
buffer (全局)
├─ recv_buffer: [kNumRanks, kNumMaxTokensPerRank] ← 所有 rank 的接收 buffer
└─ send_buffer: [1, kNumMaxTokensPerRank] ← RDMA 发送 buffer(仅 scale-out)
Elastic 的 5-Layer 映射
| Blog Buffer 层 | Elastic 源码对应 | 代码证据 |
|---|---|---|
| Token Buffer | x (输入 tensor) |
dispatch.cuh:33 — kernel 参数 |
| Dispatch Buffer | send_buffer (RDMA 发送侧) / tma_buffer (smem 中转) |
dispatch.cuh:267 — send_buffer 用于 RDMA put |
| Chunk Buffer | ❌ 无显式 Chunk Buffer | Elastic 用 kNumDispatchWarps 并行 + TMA 直接传输 |
| Receive Buffer | recv_buffer.get_rank_buffer(rank_idx) |
dispatch.cuh:268 — 每个 rank 的接收区 |
| Expert Buffer | recv_x (copy epilogue 输出) |
dispatch_copy_epilogue.cuh:139 — TMA store 到 recv_x |
Elastic 消除 Chunk 的关键机制
Elastic 通过以下方式消除了显式 Chunk Buffer:
- Warp-level 并行: 每个 dispatch warp 独立处理一个 token,无需等待 Chunk 聚合
- TMA 硬件加速: 单 token 即可发起 TMA store,硬件处理小数据包
- NCCL Symmetric Window:
gin.put<team_t>()直接写入远端recv_buffer,无需中间暂存
// deep_ep/include/deep_ep/impls/dispatch.cuh: 373-378
// Issue TMA NVLink stores: 单 token 直接发送到远端
const auto dst_ptr = stored_dst_slot_idx >= 0 ?
gin.get_sym_ptr<team_t>(recv_buffer.get_token_buffer(stored_dst_slot_idx).get_base_ptr(),
stored_dst_rank_idx) : nullptr;
if (dst_ptr != nullptr)
ptx::tma_store_1d(dst_ptr, tma_buffer.get_base_ptr(), tma_buffer.get_num_bytes<false>());
2.3 Low-Latency Kernel 的 Buffer 路径
// csrc/kernels/legacy/internode_ll.cu: 260-262
// 直接 RDMA put 到远端 rdma_recv_x,无中间 buffer
const auto dst_ptr = reinterpret_cast<uint64_t>(rdma_recv_x) +
dst_expert_local_idx * num_ranks * num_max_dispatch_tokens_per_rank * num_bytes_per_msg +
rank * num_max_dispatch_tokens_per_rank * num_bytes_per_msg + slot_idx * num_bytes_per_msg;
nvshmemi_ibgda_put_nbi_warp(dst_ptr, src_ptr, num_bytes_per_msg, dst_rank, ...);
Low-Latency 路径映射:
| Blog Buffer 层 | Low-Latency 源码对应 |
|---|---|
| Token Buffer | x (输入 tensor) |
| Dispatch Buffer | ❌ 无(直接 cast + send) |
| Chunk Buffer | ❌ 无(单 token 直接发送) |
| Receive Buffer | rdma_recv_x[expert][rank][slot] |
| Expert Buffer | packed_recv_x (按 expert 组织) |
// csrc/kernels/legacy/internode_ll.cu: 129-136
// 接收端 buffer 结构: [num_local_experts, num_ranks, num_max_tokens_per_rank]
__global__ void dispatch(void* packed_recv_x,
void* packed_recv_x_scales,
int* packed_recv_src_info,
int64_t* packed_recv_layout_range,
int* packed_recv_count,
...
void* rdma_recv_x, // ← Receive Buffer
int* rdma_recv_count,
void* rdma_x, // ← Token Buffer (send 侧)
...);
3. DeepGEMM Mega MoE:SymmBuffer 的实现
3.1 SymmBuffer 整体布局
// deep_gemm/include/deep_gemm/layout/mega_moe.cuh: 331-443
struct MegaMoEBuffer {
Workspace workspace;
Buffer input_token_buffer, input_sf_buffer, input_topk_idx_buffer, input_topk_weights_buffer;
Buffer shared_l1_token_buffer, shared_l1_sf_buffer, shared_l2_token_buffer, shared_l2_sf_buffer;
Buffer l1_token_buffer, l1_sf_buffer, l1_topk_weights_buffer;
Buffer l2_token_buffer, l2_sf_buffer;
Buffer combine_token_buffer;
};
内存布局(从低到高):
SymmBuffer (单一大块对称内存)
┌─────────────────────────────────────────────────────────────┐
│ Workspace │
│ ├─ Grid Sync Counters (128 bytes) │
│ ├─ Expert Send Count (num_experts × 8) │
│ ├─ Expert Recv Count (num_ranks × num_experts_per_rank × 8) │
│ ├─ Expert Recv Count Sum │
│ ├─ L1/L2 Full/Empty Ring Counts │
│ ├─ Src Token-Topk Idx (dispatch pulling 用) │
│ └─ Token Src Metadata (combine write-back 用) │
├─────────────────────────────────────────────────────────────┤
│ Input Token Buffer (FP8, [num_max_tokens_per_rank, hidden]) │
├─────────────────────────────────────────────────────────────┤
│ Input SF Buffer (per-32 UE8M0) │
├─────────────────────────────────────────────────────────────┤
│ Input TopK Idx Buffer (int64) │
├─────────────────────────────────────────────────────────────┤
│ Input TopK Weights Buffer (float32) │
├─────────────────────────────────────────────────────────────┤
│ Shared Expert Buffers (L1/L2) │
├─────────────────────────────────────────────────────────────┤
│ L1 Token Buffer (FP8, ring, [num_ring_tokens, hidden]) │
├─────────────────────────────────────────────────────────────┤
│ L1 SF Buffer (M-major, UTCCP 128-aligned) │
├─────────────────────────────────────────────────────────────┤
│ L1 TopK Weights Buffer (per-pool-token) │
├─────────────────────────────────────────────────────────────┤
│ L2 Token Buffer (FP8, ring) │
├─────────────────────────────────────────────────────────────┤
│ L2 SF Buffer (M-major) │
├─────────────────────────────────────────────────────────────┤
│ Combine Token Buffer (BF16, [num_topk, num_max_tokens, hidden])│
└─────────────────────────────────────────────────────────────┘
3.2 Dispatch 阶段的 Buffer 使用
// sm100_fp8_fp4_mega_moe.cuh: 533-555
// Dispatch warp 从 remote rank 的 input_token_buffer 直接拉取
const auto src_base_ptr = sym_buffer.map(
buffer.input_token_buffer.get_data_buffer(src_token_idx).get_base_ptr(),
current_rank_in_expert_idx); // ← 映射到 remote rank 地址空间
const auto dst_base_ptr = buffer.l1_token_buffer.get_data_buffer(pool_token_idx % kNumRingTokens).get_base_ptr();
// TMA load from remote, store to local L1 ring buffer
for (uint32_t i = 0; i < kNumChunks; ++ i) {
ptx::tma_load_1d(pull_buffer.get_base_ptr(),
math::advance_ptr(src_base_ptr, i * kNumBytesPerPull),
pull_mbarrier, kNumBytesPerPull);
ptx::mbarrier_arrive_and_set_tx(pull_mbarrier, kNumBytesPerPull);
issue_and_wait_pull_store(i);
}
3.3 Combine 阶段的 Buffer 使用
// sm100_fp8_fp4_mega_moe.cuh: 1294-1299
// Epilogue warp 将 L2 输出写回 remote rank 的 combine buffer
const auto dst_token = buffer.combine_token_buffer
.get_rank_buffer(dst_topk_idx)
.get_data_buffer(dst_token_idx);
const auto dst_ptr = math::advance_ptr<float4>(
dst_token.get_base_ptr(),
n_idx * sizeof(nv_bfloat16) + (lane_idx % 16) * sizeof(float4));
*sym_buffer.map(dst_ptr, dst_rank_idx) = packed; // ← NVLink write-back
// sm100_fp8_fp4_mega_moe.cuh: 1386-1449
// Combine: 从 combine_token_buffer 读取 top-k 结果,reduce 后写回 y
for (uint32_t token_idx = sm_idx * kNumEpilogueWarps + epilogue_warp_idx;
token_idx < num_tokens; token_idx += kNumSMs * kNumEpilogueWarps) {
// 读取 top-k slot indices
const int stored_topk_slot_idx = lane_idx < kNumTopk ?
static_cast<int>(__ldg(buffer.input_topk_idx_buffer.get_base_ptr<int64_t>() + ...)) : -1;
// 对每个 chunk: 加载 top-k → accumulate → cast → store
for (uint32_t chunk = 0; chunk < kNumChunks; ++ chunk) {
// 加载所有 top-k 贡献
while (move_mask_and_load(load_stage_idx)) {
combine_load_barriers[load_stage_idx]->wait(combine_phase);
for (uint32_t j = 0; j < kNumUint4PerLane; ++ j)
ptx::accumulate(reduced[j * kNumElemsPerUint4 + l], bf16_values[l]);
}
// Cast + TMA store 到输出 y
ptx::tma_store_1d(math::advance_ptr(y, token_idx * kNumHiddenBytes + chunk_byte_offset),
combine_store_buffer, kNumChunkBytes);
}
}
3.4 SymmBuffer 的 5-Layer 映射
| Blog Buffer 层 | SymmBuffer 对应段 | 代码证据 |
|---|---|---|
| Token Buffer | input_token_buffer + input_sf_buffer + input_topk_idx_buffer + input_topk_weights_buffer |
mega_moe.cuh:387-398 |
| Dispatch Buffer | ❌ 无独立对应 | dispatch warps 直接 NVLink pull |
| Chunk Buffer | ❌ 无独立对应 | token 级直接传输,无聚合 |
| Receive Buffer | l1_token_buffer (ring pool) |
mega_moe.cuh:413-414 — 接收目标 |
| Expert Buffer (L1) | l1_token_buffer + l1_sf_buffer + l1_topk_weights_buffer |
mega_moe.cuh:413-424 |
| Expert Buffer (L2) | l2_token_buffer + l2_sf_buffer |
mega_moe.cuh:426-431 |
| Combine Buffer | combine_token_buffer (BF16) |
mega_moe.cuh:433-435 |
4. 三方对比表
4.1 Buffer 层级对比
| 维度 | DeepEP Legacy | DeepEP Elastic | DeepGEMM Mega MoE |
|---|---|---|---|
| Buffer 层级 | 5 层 (Token→Dispatch→Chunk→Receive→Expert) | 4 层 (无显式 Chunk) | 3 层 (Input→Pool→Combine) |
| Token Buffer | x (输入 tensor) |
x (输入 tensor) |
input_token_buffer |
| Dispatch Buffer | rdma_channel_data.send_buffer |
send_buffer (仅 RDMA) |
❌ 无 |
| Chunk Buffer | num_max_rdma_chunked_recv_tokens FIFO |
❌ 无 | ❌ 无 |
| Receive Buffer | rdma_channel_data.recv_buffer |
recv_buffer[rank] |
l1_token_buffer (ring pool) |
| Expert Buffer | recv_x (按 expert 重排) |
recv_x (copy epilogue) |
l1/l2_token_buffer |
| Combine Buffer | recv_x (combine 输出) |
recv_x (combine 输出) |
combine_token_buffer (BF16) |
| Normal/LL 分离 | 两套 kernel + buffer | 两套 kernel + buffer | 单一套 buffer |
| 通信模型 | Push (source 主动发送) | Push (source 主动发送) | Pull (dest 主动拉取) |
| 内存模型 | NVSHMEM symmetric | NCCL Symmetric Window | Symmetric Memory + 地址映射 |
| Chunk 聚合 | 有 (FIFO 滑动窗口) | 无 (warp 并行 + TMA) | 无 (token 级 pull) |
4.2 通信范式对比
DeepEP Legacy (Push):
Source GPU: "我要发 token X 给 Dest GPU"
→ 写入本地 send_buffer
→ RDMA/NVLink put 到 Dest 的 recv_buffer
→ Dest 从 recv_buffer 读取
DeepEP Elastic (Push + TMA):
Source GPU: "我要发 token X 给 Dest GPU"
→ TMA store 直接写入 Dest 的 recv_buffer (NVLink)
→ 或写入本地 send_buffer + RDMA put (scale-out)
DeepGEMM Mega MoE (Pull):
Dest GPU: "我需要 token X 从 Source GPU"
→ TMA load 从 Source 的 input_token_buffer 直接拉取
→ 写入本地 l1_token_buffer (ring pool)
4.3 关键差异总结
| 差异点 | DeepEP | Mega MoE |
|---|---|---|
| Dispatch 方式 | 显式 Dispatch Kernel + 中间 buffer | Warp 直接 NVLink pull,无中间 buffer |
| Chunk 聚合 | Legacy 有(Normal Kernel) | 无(token 级) |
| Receive/Expert 分离 | 两个独立 buffer | 融合为 ring pool |
| Normal/LL 分离 | 两套 kernel + buffer | 单一套 buffer |
| 通信方向 | Push | Pull |
| 地址映射 | NVSHMEM 物理地址 | SymBuffer.map() 逻辑映射 |
| 同步粒度 | Chunk 级 (FIFO) | Token 级 (arrival count) |
5. Buffer 与其他子系统的交叉引用
5.1 Buffer ↔ Dispatch/Combine (Agent 1)
Dispatch 是 Buffer 的写入者:
// dispatch.cuh: 362-378
// Dispatch warp 将 token 写入 send_buffer 或远端 recv_buffer
auto send_buffer_ptr = send_buffer.get_token_buffer(token_idx).get_base_ptr();
if constexpr (not kIsScaleupNVLink) {
ptx::tma_store_1d(send_buffer_ptr, tma_buffer.get_base_ptr(), ...); // → send_buffer
}
// NVLink: 直接写入远端 recv_buffer
ptx::tma_store_1d(dst_ptr, tma_buffer.get_base_ptr(), ...);
Combine 是 Buffer 的读取者:
// combine.cuh: 96-106
// Combine warp 从 recv_buffer 读取 token,写回 send_buffer 或远端
layout::TokenLayout master_token_buffer = [=]() {
if (nvlink_bypass) {
auto token_buffer = recv_buffer.get_rank_buffer(...).get_token_buffer(src_token_idx);
token_buffer.set_base_ptr(gin.get_sym_ptr<team_t>(token_buffer.get_base_ptr(), src_rank_idx));
return token_buffer;
}
return send_buffer.get_rank_buffer(src_rank_idx).get_token_buffer(src_token_idx);
}();
Mega MoE 的融合:
// sm100_fp8_fp4_mega_moe.cuh: 533-598
// Dispatch pull + L1 buffer 写入 在同一 kernel 内完成
const auto src_base_ptr = sym_buffer.map(
buffer.input_token_buffer.get_data_buffer(src_token_idx).get_base_ptr(),
current_rank_in_expert_idx);
const auto dst_base_ptr = buffer.l1_token_buffer.get_data_buffer(pool_token_idx % kNumRingTokens).get_base_ptr();
// TMA load from remote → TMA store to local L1
5.2 Buffer ↔ Chunk Streaming (Agent 9)
Legacy 的 Chunk Streaming:
// internode.cu: 526-530
// Chunk 是一个 FIFO 滑动窗口
auto rdma_channel_data = SymBuffer<uint8_t>(
rdma_buffer_ptr, num_max_rdma_chunked_recv_tokens * num_bytes_per_token, ...);
// head/tail 控制 chunk 的发送和释放
Elastic 的替代:
Elastic 用 warp 并行 + TMA 替代 Chunk Streaming:
- 每个 warp 独立处理 token,无需等待 Chunk 聚合
- TMA store 直接发起,硬件处理流水线
Mega MoE 的替代:
Mega MoE 用 token 级 pull + ring buffer 替代 Chunk Streaming:
- Dispatch warps 按全局 token 索引轮转处理
- 每个 token 独立通过 NVLink pull
- Ring buffer 的 full/empty count 控制流式传输
// sm100_fp8_fp4_mega_moe.cuh: 526-531
// Ring buffer 空位检查 (替代 Chunk FIFO)
const auto l1_empty_count_target = (pool_block_idx / kNumRingBlocks) * kNumL1BlockNs;
if (l1_empty_count_target > 0) {
const auto empty_ptr = workspace.get_l1_empty_count_ptr(pool_block_idx % kNumRingBlocks);
while (ptx::ld_acq(empty_ptr) < l1_empty_count_target); // 等待空位
}
5.3 Buffer ↔ FIFO (Agent 4)
Legacy FIFO 机制:
// internode.cu: 565-567
__shared__ int rdma_send_channel_tail[kNumRDMARanks];
__shared__ uint32_t rdma_send_channel_window[kNumRDMARanks];
// FIFO head/tail 控制 chunk 发送窗口
Elastic 的替代:
Elastic 用 NCCL Symmetric Window + GPU Barrier 替代 FIFO:
- comm::gpu_barrier<kIsScaleupNVLink, ...>() 提供全局同步
- 无需显式 FIFO,依赖 NCCL 的流控
Mega MoE 的 Ring Buffer:
// mega_moe.cuh: 98-111
// L1/L2 ring buffer 的 full/empty count
num_bytes += num_ring_blocks * sizeof(uint32_t); // L1 full count
num_bytes += num_ring_blocks * sizeof(uint32_t); // L1 empty count
num_bytes += num_ring_blocks * sizeof(uint32_t); // L2 full count
num_bytes += num_ring_blocks * sizeof(uint32_t); // L2 empty count
// sm100_fp8_fp4_mega_moe.cuh: 593-596
// 写入完成后增加 full count
ptx::red_add_rel(workspace.get_l1_full_count_ptr(pool_block_idx % kNumRingBlocks),
is_last_token ? BLOCK_M - (token_idx_in_expert % BLOCK_M) : 1u);
6. Buffer 层级 Mermaid 图
6.1 DeepEP Legacy 5-Layer Buffer (Mermaid)
flowchart TB
subgraph Legacy_Normal["Legacy Normal Kernel (Throughput)"]
direction TB
TB["Token Buffer<br/>x [num_tokens, hidden]<br/>Token-major"]
DB["Dispatch Buffer<br/>rdma_channel_data.send_buffer<br/>Token-major → Dest-major"]
CB["Chunk Buffer<br/>FIFO sliding window<br/>num_max_rdma_chunked_recv_tokens<br/>Token=scheduling, Chunk=communication"]
NB["Network<br/>NVLink + RDMA"]
RB["Receive Buffer<br/>rdma_channel_data.recv_buffer<br/>Dest GPU receives chunks"]
EB["Expert Buffer<br/>recv_x [num_recv_tokens, hidden]<br/>Dest-major → Expert-major"]
TB --> DB --> CB --> NB --> RB --> EB
end
subgraph Legacy_LL["Legacy Low-Latency Kernel (Decode)"]
direction TB
TB2["Token Buffer<br/>x [num_tokens, hidden]"]
NB2["Direct RDMA<br/>IBGDA put"]
RB2["Receive Buffer<br/>rdma_recv_x<br/>[expert, rank, slot]"]
EB2["Expert Buffer<br/>packed_recv_x<br/>[expert, num_ranks, num_max_tokens]"]
TB2 --> NB2 --> RB2 --> EB2
end
6.2 DeepEP Elastic Buffer (Mermaid)
flowchart TB
subgraph Elastic_Dispatch["Elastic Dispatch Phase"]
direction TB
X["x (输入 tensor)<br/>Token-major"]
TMA["tma_buffer (smem)<br/>TMA load from x"]
SB["send_buffer<br/>RDMA 发送侧<br/>[1, kNumMaxTokensPerRank]"]
NB["NCCL Symmetric Window<br/>NVLink / RDMA"]
RB["recv_buffer<br/>[kNumRanks, kNumMaxTokensPerRank]<br/>每个 rank 的接收区"]
X --> TMA
TMA -->|NVLink TMA store| RB
TMA -->|RDMA put| SB --> NB --> RB
end
subgraph Elastic_Epilogue["Elastic Copy Epilogue"]
direction TB
RB2["recv_buffer[rank_idx]<br/>Dest-major"]
TMA2["tma_buffer (smem)<br/>TMA load from recv_buffer"]
RX["recv_x<br/>Dest-major → Expert-major<br/>(或 Expand 模式)"]
RB2 --> TMA2 --> RX
end
6.3 DeepGEMM SymmBuffer (Mermaid)
flowchart TB
subgraph Input_Phase["Input Phase (每个 rank 本地)"]
X["x (FP8 tokens) + x_sf<br/>+ topk_idx + topk_weights<br/>→ input_token_buffer"]
end
subgraph Dispatch_Phase["Dispatch Phase (跨 rank)"]
PULL["Dispatch Warps<br/>NVLink TMA Load<br/>从 remote rank 拉取 token<br/>sym_buffer.map(input_token_buffer, dst_rank)"]
META["Metadata 交换<br/>expert_send_count<br/>src_token_topk_idx"]
end
subgraph L1_Pool["L1 Expert Ring Pool"]
L1T["l1_token_buffer<br/>FP8, ring<br/>[num_ring_tokens, hidden]"]
L1SF["l1_sf_buffer<br/>M-major, UTCCP 128-aligned"]
L1W["l1_topk_weights"]
end
subgraph L1_GEMM["L1 GEMM + SwiGLU"]
GEMM1["FP8×FP4 GEMM<br/>block-scaled MMA"]
SWIGLU["SwiGLU + topk_weight<br/>in epilogue"]
end
subgraph L2_Pool["L2 Expert Ring Pool"]
L2T["l2_token_buffer<br/>FP8, ring"]
L2SF["l2_sf_buffer<br/>M-major"]
end
subgraph L2_GEMM["L2 GEMM"]
GEMM2["FP8×FP4 GEMM<br/>输出 BF16"]
end
subgraph Combine_Phase["Combine Phase"]
CB["combine_token_buffer<br/>BF16, [num_topk, num_max_tokens, hidden]<br/>per-topk-slot"]
REDUCE["Top-K Reduce<br/>累加 + TMA store 到 y"]
end
X --> PULL --> L1T
META --> PULL
L1T & L1SF & L1W --> GEMM1 --> SWIGLU --> L2T
L2T & L2SF --> GEMM2 --> CB --> REDUCE
6.4 三方对比 Mermaid (Mermaid)
flowchart LR
subgraph EP_Legacy["DeepEP Legacy 5-Layer"]
direction TB
A1["Token Buffer<br/>x"] --> A2["Dispatch Buffer<br/>send_buffer"] --> A3["Chunk Buffer<br/>FIFO window"]
A3 --> A4["Network"] --> A5["Receive Buffer<br/>recv_buffer"] --> A6["Expert Buffer<br/>recv_x"]
end
subgraph EP_Elastic["DeepEP Elastic 4-Layer"]
direction TB
B1["Token Buffer<br/>x"] --> B2["tma_buffer (smem)"]
B2 -->|NVLink| B3["Receive Buffer<br/>recv_buffer[rank]"]
B2 -->|RDMA| B2a["send_buffer"] --> B3
B3 --> B4["Expert Buffer<br/>recv_x (copy epilogue)"]
end
subgraph MM["DeepGEMM Mega MoE 3-Layer"]
direction TB
C1["Token Buffer<br/>input_token_buffer"] -->|NVLink pull| C2["L1 Ring Pool<br/>l1_token_buffer<br/>(Receive + Expert 融合)"]
C2 --> C3["L2 Ring Pool<br/>l2_token_buffer"]
C3 --> C4["Combine Buffer<br/>combine_token_buffer"]
end
A1 -.-> B1
A5 -.-> B3
A6 -.-> B4
B1 -.-> C1
B3 -.-> C2
B4 -.-> C3
7. 核心洞察
7.1 Buffer 演化的主线
Legacy (5 层) → Elastic (4 层) → Mega MoE (3 层)
↓ ↓ ↓
Chunk 有 Chunk 无 Chunk 无
Dispatch 有 Dispatch 弱 Dispatch 无
Receive/Expert 分离 Receive/Expert 分离 Receive/Expert 融合
Push 模型 Push 模型 Pull 模型
7.2 为什么 Mega MoE 可以消除 Dispatch/Chunk Buffer
- Symmetric Memory 的地址对称性: 任何 rank 可以直接访问任何其他 rank 的 buffer,无需 "打包-发送-解包"
- Persistent Kernel + Warp Specialization: dispatch warps 和 GEMM warps 同时运行,dispatch 可以按需实时 pull
- NVLink 的全连接拓扑: 节点内所有 GPU 两两直连,pull 和 send 的延迟差异不大
- Decode 场景为主: 小 batch 下 chunk 聚合无收益,直接 token 级传输更优
7.3 Pull vs Push 的范式转变
| 维度 | Push (DeepEP) | Pull (Mega MoE) |
|---|---|---|
| 控制权 | Source 决定何时发送 | Dest 决定何时拉取 |
| 速率控制 | 发送方可能溢出接收方 | 接收方天然控制速率 |
| 负载均衡 | 需要额外机制 | 天然均衡(处理快的 rank 多 pull) |
| 同步 | 全局 "发送完毕" 信号 | per-block arrival count |
| Buffer 层级 | 需要 Dispatch Buffer 暂存 | 无需中间 buffer |
7.4 Ring Buffer vs FIFO
| 维度 | Legacy FIFO | Mega MoE Ring Buffer |
|---|---|---|
| 粒度 | Chunk 级 | Token 级 |
| 同步 | head/tail 原子操作 | full/empty count |
| 容量 | num_max_rdma_chunked_recv_tokens |
num_ring_tokens (per expert) |
| 复用 | 单次使用 | 环形复用 |
| 与 GEMM 集成 | 分离 | 紧密集成 (full count 触发 GEMM) |
8. 代码索引
| 文件 | 关键内容 |
|---|---|
deep_ep/buffers/elastic.py |
ElasticBuffer Python 类 |
deep_ep/buffers/legacy.py |
Legacy Buffer Python 类 |
deep_ep/include/deep_ep/common/layout.cuh |
TokenLayout, BufferLayout, WorkspaceLayout |
csrc/kernels/legacy/buffer.cuh |
Legacy SymBuffer / AsymBuffer |
csrc/kernels/legacy/internode.cu |
Legacy internode dispatch (5-Layer 完整实现) |
csrc/kernels/legacy/internode_ll.cu |
Legacy low-latency dispatch |
csrc/kernels/elastic/dispatch.hpp |
Elastic dispatch 启动逻辑 |
deep_ep/include/deep_ep/impls/dispatch.cuh |
Elastic dispatch kernel (buffer 使用) |
deep_ep/include/deep_ep/impls/dispatch_copy_epilogue.cuh |
Elastic copy epilogue (Expert Buffer 生成) |
deep_ep/include/deep_ep/impls/combine.cuh |
Elastic combine kernel (buffer 使用) |
deep_gemm/include/deep_gemm/layout/sym_buffer.cuh |
SymBuffer 地址映射 |
deep_gemm/include/deep_gemm/layout/mega_moe.cuh |
MegaMoEBuffer 完整布局 |
deep_gemm/mega/__init__.py |
SymmBuffer Python 类 |
deep_gemm/include/deep_gemm/impls/sm100_fp8_fp4_mega_moe.cuh |
Mega MoE kernel (dispatch/GEMM/combine) |
9. 参考源码片段
9.1 Legacy 5-Layer 完整数据流
// internode.cu: 631-632
// Token → Dispatch Buffer (send_buffer)
auto send_buffer = lane_id == rdma_rank ?
rdma_channel_data.recv_buffer(lane_id) : // 本地: recv_buffer 复用
rdma_channel_data.send_buffer(lane_id); // 远端: send_buffer
// internode.cu: 647-648
// Chunk: FIFO 滑动窗口控制
while (rdma_tail_idx - cached_rdma_channel_head >= num_max_rdma_chunked_recv_tokens);
// internode.cu: 617-624
// Network: RDMA put 到远端 recv_buffer
nvshmemi_ibgda_put_nbi_warp<true>(..., rdma_channel_meta.recv_buffer(rdma_rank), ...);
// internode.cu: 453
// Expert Buffer: recv_x (输出 tensor)
__global__ void dispatch(int4* recv_x, float* recv_x_scales, ...);
9.2 Elastic Buffer 分配
// dispatch.cuh: 263-268
const auto token_layout = layout::TokenLayout(kNumHiddenBytes, ...);
auto tma_buffer = layout::BufferLayout<true>(token_layout, kNumDispatchWarps, 1, ...);
auto recv_buffer = layout::BufferLayout<false>(token_layout, kNumRanks, kNumMaxTokensPerRank, buffer);
auto send_buffer = layout::BufferLayout<false>(token_layout, 1, kNumMaxTokensPerRank,
recv_buffer.get_buffer_end_ptr());
9.3 Mega MoE SymmBuffer 分配
// mega_moe.cuh: 387-435
input_token_buffer = Buffer(input_token_layout, 1, num_max_tokens_per_rank, workspace.get_end_ptr());
input_sf_buffer = Buffer(input_sf_layout, 1, num_max_tokens_per_rank, input_token_buffer.get_end_ptr());
input_topk_idx_buffer = Buffer(input_topk_idx_layout, 1, num_max_tokens_per_rank, ...);
input_topk_weights_buffer = Buffer(input_topk_weights_layout, 1, num_max_tokens_per_rank, ...);
l1_token_buffer = Buffer(input_token_layout, 1, num_ring_tokens, ...);
l1_sf_buffer = Buffer(input_sf_layout, 1, num_sf_ring_tokens, ...);
l2_token_buffer = Buffer(intermediate_token_layout, 1, num_ring_tokens, ...);
l2_sf_buffer = Buffer(intermediate_sf_layout, 1, num_sf_ring_tokens, ...);
combine_token_buffer = Buffer(bf16_token_layout, num_topk, num_max_tokens_per_rank, ...);
9.4 Mega MoE Dispatch Pull
// sm100_fp8_fp4_mega_moe.cuh: 533-555
const auto src_base_ptr = sym_buffer.map(
buffer.input_token_buffer.get_data_buffer(src_token_idx).get_base_ptr(),
current_rank_in_expert_idx);
const auto dst_base_ptr = buffer.l1_token_buffer.get_data_buffer(pool_token_idx % kNumRingTokens).get_base_ptr();
for (uint32_t i = 0; i < kNumChunks; ++ i) {
ptx::tma_load_1d(pull_buffer.get_base_ptr(),
math::advance_ptr(src_base_ptr, i * kNumBytesPerPull),
pull_mbarrier, kNumBytesPerPull);
ptx::mbarrier_arrive_and_set_tx(pull_mbarrier, kNumBytesPerPull);
issue_and_wait_pull_store(i);
}
9.5 Mega MoE Combine Write-back
// sm100_fp8_fp4_mega_moe.cuh: 1294-1299
const auto dst_token = buffer.combine_token_buffer
.get_rank_buffer(dst_topk_idx)
.get_data_buffer(dst_token_idx);
const auto dst_ptr = math::advance_ptr<float4>(
dst_token.get_base_ptr(),
n_idx * sizeof(nv_bfloat16) + (lane_idx % 16) * sizeof(float4));
*sym_buffer.map(dst_ptr, dst_rank_idx) = packed;
分析基于 DeepEP 源码 (legacy + elastic) 与 DeepGEMM Mega MoE 源码,对照博客文本 Section 2