DeepSeek DeepEP解析:两阶段All-to-All通信中的Notify Dispatch元数据计算
1. DeepSeek DeepEP框架与两阶段All-to-All通信
在分布式机器学习训练中,All-to-All通信是一个关键的性能瓶颈。DeepSeek团队开发的DeepEP框架针对这一挑战提出了创新的两阶段All-to-All算法,特别适用于追求高带宽的Mixture-of-Experts(MoE)模型训练场景。
传统All-to-All算子在发送数据到同一台机器的不同rank时,会存在重复token发送的问题。DeepSeek的解决方案充分利用了现代GPU集群的硬件特性:机内NVLink带宽远高于机间RDMA带宽。基于这一观察,他们设计了两阶段通信模式:
- 跨节点阶段:将发往同一机器的所有token先发送给目标机器的同号卡
- 机内转发阶段:通过目标机器的同号卡进行机内数据转发
这种设计显著减少了跨节点通信量,充分利用了机内高带宽优势。在实际测试中,这种策略可以将端到端通信时间降低30-50%,具体效果取决于集群规模和模型配置。
2. Notify Dispatch阶段的核心作用
Notify Dispatch是整个两阶段通信算法的"大脑",负责预先计算所有必要的元数据。这些元数据包括:
- 每台机器将接收多少token(num_tokens_per_rank)
- 每个专家(expert)将接收多少token(num_tokens_per_expert)
- 每个RDMA节点将接收多少token(num_tokens_per_rdma_rank)
这些信息看似简单,但在大规模分布式训练中,它们的计算和同步效率直接影响整体性能。Notify Dispatch阶段的高效实现需要考虑以下几个关键点:
- 计算与通信重叠:在计算元数据的同时,尽可能提前发起必要的通信
- 内存访问模式优化:确保对topk_idx等输入张量的访问是合并的(coalesced)
- 同步开销最小化:设计高效的机内和跨节点同步机制
3. Buffer管理与内存布局设计
DeepEP框架引入了多种Buffer类型来管理通信过程中的内存使用:
3.1 基础Buffer结构
基础Buffer管理一段连续的内存区域,主要功能包括:
- 内存分配与释放(通过cudaMalloc/cudaFree)
- IPC(Inter-Process Communication)句柄管理
- 设备间内存地址映射
class Buffer {
public:
Buffer(int rank, int num_ranks, int64_t num_nvl_bytes,
int64_t num_rdma_bytes, bool low_latency_mode);
// 获取IPC内存句柄
cudaIpcMemHandle_t get_ipc_handle() const;
// 同步各rank的Buffer状态
void sync(const std::vector<std::optional<std::string>>& all_gathered_handles,
const std::string& root_unique_id);
private:
void* buffer_ptrs[NUM_MAX_NVL_PEERS]; // 各GPU的buffer指针
cudaIpcMemHandle_t ipc_handles[NUM_MAX_NVL_PEERS]; // IPC句柄
// ... 其他成员变量
};
3.2 SymBuffer与AsymBuffer
这两种特殊的Buffer类型用于解决多SM(Streaming Multiprocessor)并发访问的同步问题:
- SymBuffer:对称缓冲区,所有SM以相同方式访问
- AsymBuffer:非对称缓冲区,不同SM访问不同偏移量
它们的典型使用场景包括:
- 跨节点通信数据的暂存(使用SymBuffer)
- 机内数据转发的中间存储(使用AsymBuffer)
4. Notify Dispatch的GPU内核实现
Notify Dispatch的核心逻辑实现在CUDA内核notify_dispatch中,采用多SM协作的执行模式:
4.1 SM0的特殊职责
SM0负责全局的元数据收集和同步工作:
- 跨节点同步:
if (sm_id == 0) {
// 全局barrier:第一个warp执行机内同步,第二个warp执行机间同步
if (thread_id == 32)
nvshmem_barrier_with_same_gpu_idx<kLowLatencyMode>(rdma_team);
barrier_device<NUM_MAX_NVL_PEERS>(task_fifo_ptrs, head, nvl_rank);
__syncthreads();
}
- 元数据收集与分发:
- 通过NVLink收集本机各GPU的统计信息
- 通过RDMA将汇总信息发送给其他节点
- 计算各专家的token分配前缀和
4.2 其他SM的工作负载
其余SM(SM1-SM7)专注于计算每个通信channel的详细元数据:
int dst_rdma_rank = sm_id - 1;
for (int channel_id = warp_id; channel_id < num_channels; channel_id += num_warps) {
// 计算该channel处理的token范围
get_channel_task_range(num_tokens, num_channels, channel_id,
token_start_idx, token_end_idx);
// 统计该channel向目标节点各GPU发送的token数
int total_count = 0, per_nvl_rank_count[NUM_MAX_NVL_PEERS] = {0};
for (int64_t i = token_start_idx + lane_id; i < token_end_idx; i += 32) {
auto is_token_in_rank_values = /* 获取token分发信息 */;
#pragma unroll
for (int j = 0; j < NUM_MAX_NVL_PEERS; ++j)
per_nvl_rank_count[j] += is_token_in_rank_values[j];
total_count += (is_token_in_rank_uint64 != 0);
}
// 写入全局统计矩阵
if (lane_id == 0) {
for (int i = 0; i < NUM_MAX_NVL_PEERS; ++i)
gbl_channel_prefix_matrix[(dst_rdma_rank * NUM_MAX_NVL_PEERS + i) * num_channels + channel_id]
= per_nvl_rank_count[i];
rdma_channel_prefix_matrix[dst_rdma_rank * num_channels + channel_id] = total_count;
}
}
5. 性能优化关键技巧
在实现Notify Dispatch时,DeepSeek团队应用了多项性能优化技术:
5.1 内存访问优化
- 合并访问:确保对topk_idx等输入张量的访问是合并的
- 共享内存利用:频繁访问的中间结果缓存在共享内存
- 寄存器压力控制:通过
#pragma unroll控制循环展开程度
5.2 计算优化
- Warp级原语:使用
warp_reduce_sum等warp级操作减少同步开销 - 异步执行:将计算与通信流水线化
- 负载均衡:动态调整各SM/channel的工作量
5.3 同步优化
- 轻量级Barrier:基于原子操作的机内barrier
- 分层同步:区分机内和机间同步需求
- 同步与计算重叠:在等待同步时执行其他有用工作
6. 实际应用中的注意事项
在实际部署Notify Dispatch时,需要注意以下几点:
- CUDA Graph兼容性:由于使用了CPU-GPU同步,当前实现与CUDA Graph不兼容
- 网络配置:必须关闭网络的adaptive routing功能
- 内存对齐:专家分配需要考虑内存对齐(expert_alignment)
- 错误处理:完善的错误检查机制确保分布式执行的可靠性
一个典型的使用示例如下:
def dispatch_forward(...):
# 先计算通信布局
num_tokens_per_rank, num_tokens_per_rdma_rank, num_tokens_per_expert, is_token_in_rank, previous_event = \
_buffer.get_dispatch_layout(...)
# 执行实际的MoE分发
recv_x, recv_topk_idx, recv_topk_weights, num_recv_tokens_per_expert_list, handle, event = \
_buffer.dispatch(...)
return recv_x, recv_topk_idx, recv_topk_weights, num_recv_tokens_per_expert_list, handle, event
7. 扩展与未来方向
当前实现已经展示了卓越的性能,但仍有一些潜在的优化方向:
- 动态负载均衡:根据实时网络状况调整通信策略
- 拓扑感知:考虑实际的网络拓扑结构优化通信路径
- 混合精度支持:探索FP16/BF16等低精度通信
- 故障恢复:增强在节点故障情况下的恢复能力
在实际的MoE模型训练中,我们观察到Notify Dispatch阶段通常只占整个通信时间的10-15%,但它为后续的高效数据分发奠定了基础。通过精细调整元数据计算和同步的各个环节,可以在大规模分布式训练中获得显著的端到端性能提升。
更多推荐


所有评论(0)