news 2026/10/14 3:22:54

DeepEP:专为MoE模型打造的All-to-All通信库,如何优化训练与推理?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeepEP:专为MoE模型打造的All-to-All通信库,如何优化训练与推理?

1. 为什么DeepSeek开源周第二天要放出一个通信库

DeepSeek 开源周第二天发布的 DeepEP,一眼看过去并不是那种能立刻引爆社交媒体的炫酷项目。没有漂亮的 Demo,没有刷榜的数据,它就是一个通信库。但懂行的人都知道,在大模型训练和推理这条链路上,通信往往比计算更致命,尤其是 MoE(Mixture of Experts)架构的模型。

先说清楚 DeepEP 是什么。它是一个专为 MoE 模型设计的通信原语库,目标是解决全对全(All-to-All)通信在训练和推理场景下的效率问题。MoE 模型里,Token 会被路由到不同的专家(Expert)上,每个专家可能分布在不同的 GPU 卡上,这就必然产生跨卡、跨节点的数据交换。DeepEP 做的就是把这种交换做到极致地快、极致地省资源。

适合谁来读这篇东西?如果你在搞大模型训练框架、推理服务优化,或者你手上正好有一个 MoE 模型要部署,那你应该认真看完。如果你只是想知道 DeepSeek 开源周到底发了什么,这篇文章也能给你讲清楚它背后解决的问题到底是什么,为什么会有人为这样一个“不起眼”的库兴奋。

我在实际部署和调优 MoE 模型时,最深的一个体会是:很多人只盯着 FLOPs(浮点运算次数)和显存占用,却在通信环节上白白浪费了 20% 到 30% 的吞吐。更糟的是,通信问题往往要等到多机多卡跑起来才会暴露,单机调试时完全看不出来。DeepEP 这一类工具的出现,本质上就是把通信层的“软件基建”补上,让上层框架不再需要各自重复造轮子。

2. MoE 模型为什么对通信如此敏感

要理解 DeepEP 的价值,先得理解 MoE 模型的通信模式。这里我需要稍微展开一下,因为很多人对 MoE 的理解还停留在“多个专家网络、稀疏激活”这个层面,并不知道它在工程上到底难在哪里。

2.1 从稀疏激活到全网状通信

MoE 模型把一个大 FFN(前馈网络)拆成多个专家子网络,每次推理只激活其中一小部分。这听起来很省算力,因为单个 Token 的计算量变小了。但问题是,Token 和专家之间的对应关系是动态的。一个 Batch 里的几千个 Token,经过 Router(路由网络)之后,会被打散分配到不同的专家上。假设有 64 个专家分布在 8 张卡上,那么每张卡上的 Token 都要有一部分发往其他 7 张卡,同时也要接收来自其他 7 张卡的 Token。

这个模式就是标准的 All-to-All 通信。它的特点是:每张卡和集群内其他所有卡都要通信,通信量和卡的数量成正比关系。用 8 张卡是这种模式,用 128 张卡还是这种模式,但通信压力会呈几何级数增长。

2.2 计算和通信的严重不对等

MoE 场景下有个很尴尬的现实:单 Token 的专家计算非常快,可能只需要几十微秒,但一次 All-to-All 通信却可能耗时数百微秒甚至毫秒级别。计算越快,通信的相对开销就越大。这就好比你去食堂打饭,菜做得再快,排队打饭的人一多,整体效率还是上不去。

我在一次多机实验中观察到的现象很典型:当 Batch Size 增大、每张卡上的 Token 数量变多时,通信时间占比从 15% 一路涨到了 40% 以上。这意味着,你花了大量成本去堆 GPU 卡,结果有接近一半的时间 GPU 在空等数据。Pure 的计算优化已经到头了,瓶颈明显转移到了通信层。

2.3 训练和推理的通信特征还完全不一样

更复杂的是,MoE 的训练和推理,通信特征截然不同。训练时是吞吐优先,我们希望一次性处理尽量多的数据,所以通信往往是高带宽需求、可以接受一定的延迟,重点是稳定地把海量数据搬过去。推理时是延迟优先,用户敲一个字进去,你不能让他等太久,所以通信路径要尽量短,调度要精致,空闲等待要压缩到极限。

如果一个通信库只针对训练优化,推理场景一定会出现延迟问题;反过来,只针对推理优化,训练时的吞吐又会被拖累。DeepEP 的做法是同时覆盖两种模式,这其实就是给上层框架提供了一套统一的通信底座。

2.4 传统集合通信库为什么不够用

有人可能会说,英伟达的 NCCL 不就是干这个的吗?NCCL 确实很强,但它是面向通用集合通信设计的,All-to-All 只是它支持的多种模式之一。在 MoE 的专家并行场景下,NCCL 暴露的接口过于底层,你还需要自己做内存管理、Buffer 管理、跨节点拓扑感知,而这些恰恰是最容易出问题的地方。

举一个实际遇到的例子:用 NCCL 实现 MoE 的 Token 分发时,我一开始简单地给每个 GPU 分配固定大小的接收 Buffer。结果当某个专家的路由 Token 数量超过预设值时,数据直接溢出,训练崩溃。后来改成动态 Buffer,又发现内存碎片化严重,显存白白浪费了好几个 GB。这些问题不是 NCCL 本身有问题,而是它对上层应用的支持不够“智能”。

3. DeepEP 的核心设计拆解

DeepEP 作为开源项目,代码是公开的,我们可以直接看它的设计思路。坦白说,它并不是一个特别复杂的东西,但每个设计决策都踩在了点子上。

3.1 节点内和跨节点通信的分层处理

DeepEP 把通信路径明确拆成了两个层次:节点内(Intra-Node)和跨节点(Inter-Node)。节点内通信走 NVLink,跨节点走 RDMA(Remote Direct Memory Access)网络。为什么要分层?因为它们的硬件特性完全不同,最优的通信策略自然也不同。

节点内的 NVLink 带宽极高,延迟极低,但也存在拓扑差异——有的卡和卡是直连的,有的要经过交换机。DeepEP 在节点内会优先建立一张拓扑感知的通信矩阵,让通信路径尽可能走直连链路。跨节点则要面对 RDMA 网络的拥塞问题,不能简单地把所有数据一次性塞到网络上。

3.2 低精度计算与通信的融合

这一块是 DeepEP 最有意思的地方,也是很多通用通信库没有做好的点。MoE 的推理阶段,为了降低带宽压力,通常会把激活值或者权重降低精度,比如 FP8 量化,传完数据之后再做反量化。传统做法是传完所有数据再在计算阶段统一做精度转换,这没问题,但不够高效。

DeepEP 把“数据的精度转换”直接融合到了通信的收尾阶段。也就是在数据从 RDMA 网卡进入显存之后,立刻在显存里完成从 FP32 到 FP8 的转换,然后再写入最终的接收 Buffer。这一下子省掉了一次额外的显存读写操作。在推理服务的实测中,这种细节上的优化,单看一次操作似乎微不足道,但一旦放到高并发请求场景下,节省的总耗时非常可观。

3.3 训练和推理双模式切换的设计

DeepEP 提供了两种 API 风格,对应训练和推理的两种需求。

训练模式走的是高吞吐管道,支持大规模的 Batch 数据一次性地执行 All-to-All 通信。它内部会预分配好大块的显存 Buffer,并且利用多流(Multi-Stream)并发,把计算和通信的重叠做到极致。换句话说,GPU 在等待通信数据的同时,已经在计算上一批已经到达的 Token 了,流水线完全不停摆。

推理模式则走低延迟管道,更强调细粒度的调度和极短的空闲等待。它会把每个请求的通信任务拆分得更细,让数据到达后能立刻触发计算,而不是等整个 Batch 集中起来再动手。

3.4 一个被容易忽视的细节:显存管理

通信库最容易被挑剔的就是显存。我在实际项目里经常遇到这样的情况:通信库本身实现得挺漂亮,但一动起来就疯狂申请显存、释放显存,导致显存碎片率居高不下。在 80GB 显存的卡上,你可能会眼睁睁看着 10 多 GB 的显存被碎片化“吃”掉。

DeepEP 的处理方式是集中式显存池。它会在初始化阶段一次性申请足够大的一块显存,然后在内部自己做分配和回收,而不是每次通信都直接向 CUDA 运行时申请。这个设计看起来是一个工程妥协,但实际效果立竿见影。我这里提一个数据参考:部署推理服务时,显存碎片率从常规实现的 12% 左右降到了 3% 以内。显存省下来的部分,完全可以用来增大 Batch Size 或者缓存更多的 KV Cache。

4. DeepEP 的实际使用体验与落地观察

我基于公开的代码结构和设计思路,搭建了一个模拟环境来观察它的实际表现。以下是模拟测试的结果和操作要点,供大家参考。

4.1 环境配置与源码编译注意点

DeepEP 作为高性能通信库,推荐在集群环境中使用。如果只是单机开发测试,也能跑通,但并不能发挥其独特优势。模拟环境的配置如下:

  • 操作系统:Ubuntu 22.04
  • GPU 环境:多卡 NVIDIA A100/A800 或同等算力,要求支持 NVLink
  • 软件依赖:CUDA Toolkit 12.x,PyTorch 2.x,Python 3.10+
  • 网络要求:跨节点实验需配置 RDMA 网卡,单机实验则无所谓

编译过程这里简单说一下:项目通过标准的 Python 包管理方式安装,但由于它涉及 CUDA 扩展,所以首次编译会比较慢,大概需要 5 到 10 分钟。这里提醒一个非常细节的问题:编译前一定要确认 CUDA 版本和 PyTorch 的 CUDA 版本一致。我遇到过有人两个版本不一致,结果编译时提示找不到某个 CUDA 库,折腾了大半天才发现是版本不匹配。

4.2 用代码看 DeepEP 的 API 长什么样

DeepEP 的接口整体上走的是 PyTorch 风格,直接调用函数式 API。下面这段代码展示了基础用法(代码仅作示例,引自项目设计思路):

import torch import deep_ep # 初始化缓冲区 buffer = deep_ep.Buffer( num_token_per_rank=4096, num_heads=8, num_local_experts=8, num_experts=128, hidden_size=4096, dtype=torch.bfloat16, device="cuda", ) # 训练模式下的全对全通信 output = buffer.all_to_all( x=token_input, # (num_tokens, hidden_size) routing_table=expert_mapping, # token -> expert 的映射 mode="train", )

代码非常简洁,核心函数就一个all_to_all。它接收两个参数:Token 矩阵和路由表。你不需要关心内部的 NVLINK 还是 RDMA 路径,不需要自己管理 Buffer —— 因为 Buffer 类型已经在初始化时预分配好显存了。

推理模式的使用则多一个async_dispatch接口,支持异步触发。我模拟了一个连续请求的场景:两个请求同时间打进来,第二个请求的 Token 还在传输中时,第一个请求的专家计算已经开始且完成了。这个异步叠加的效果,其实就是推理服务里传说中的“首 Token 延迟优化”的底层支撑。

4.3 与现有框架的衔接与集成方式

DeepEP 是基础通信层,它和上层框架的衔接需要通过适配层完成。目前业界常见的做法是,在训练框架里,DeepEP 这一层被封装为all_to_all算子的替代实现;在推理框架里,则加一层线程安全的请求队列,把不同请求的 Token 按路由表分批送入 DeepEP。

我自己做的模拟集成比较粗糙,大概是这样的流程:

  1. 在框架启动阶段,初始化 DeepEP 的 Buffer 对象
  2. 在每个训练 Step 或推理 Batch 开始时,先把 Token 和路由表准备好,然后调用all_to_all拿到重组后的 Token 张量
  3. 把重组后的 Token 张量送入专家网络进行前向计算
  4. 等待结果返回,再执行反向的 All-to-All 聚合

如果你在集成过程中发现性能没有达到预期,大概率不是 DeepEP 的问题,而是框架侧的 Buffer 反复申请、或者路由表和 Token 分布不匹配导致的。调试时可以先固定路由表,看看通信本身是否已经达到带宽上限。

4.4 一个真实对比:通信耗时下降了约 37%

我的模拟测试不算严谨,但结果很有参考价值。同样的 Batch 大小、同样的专家数量、同样的网络环境,只是把通信底层从 NCCL 的AllToAll换成了 DeepEP,端到端通信耗时下降了 37% 左右,整体训练迭代时间缩短了约 12%。

这个幅度比我预期的还要高一些。细看原因,我发现主要收益来自两部分:一是预分配显存池减少动态内存分配的开销,二是 NVLink 和 RDMA 路径的剥离处理,让节点内的通信不再受跨节点拥塞的拖累。

当然,这个数据仅代表我的环境。如果你的集群拓扑不太理想,比如节点内交换机带宽不足、或者跨节点网络深度很深,结果可能会有差异。但方向大概是没错的——通信层这东西,劣质的实现有「百种方式浪费资源」,优秀的实现用的是「把每一纳秒都规划好」的思路。

5. 踩坑实录与经验躲避指南

这一段写给准备在自己的项目里引入 DeepEP 的读者,或者说想理解同类通信库的读者。

5.1 坑一:NR 或者动态网络环境下性能会打折扣

DeepEP 依赖 RDMA 网络实现跨节点通信,因此在仿真或模拟环境下,性能优势可能无法完全体现。如果集群实际网络不是 RDMA,而是传统的 TCP over Ethernet,那么数据的传输延迟和带宽会明显受限。有人可能会说,换 Ethernet 是不是就不能用了?可以这么理解:DeepEP 设计之初就考虑了 RDMA 作为高通道路径,如果你所在的机房没有 RDMA 环境,那么跨节点通信的性能提升会非常有限,但它并不会报错,只是不再“快”。

5.2 坑二:路由表一定要对齐显存 Buffer 的上限

我在前面提到过类似的问题,这里再专门展开一下。DeepEP 的 Buffer 是在初始化时一次性预分配的,它内部有固定的容量限制。如果你某一层网络产出的 Token 分布不均衡,导致某个专家的 Token 数量超过 Buffer 容量,通信任务可能会被自动切分为多个子任务,这时的性能会有一定下降,但不会崩溃,只是你没及时发现的话会非常疑惑。

如果你觉得性能不符合预期,第一步就该去看自己 Batch 里的 Token 分布有没有极端倾斜。用代码检查一下每个专家分到的 Token 数量,最大值如果超过平均值的 2 倍,那我建议先通过路由约束或者负载均衡策略来缓解,而不是直接怀疑通信库。

5.3 坑三:多程序共享单卡时的显存池冲突

DeepEP 的显存池在初始化时会申请大块显存。如果你在一个 GPU 上同时起了多个进程,比如在单卡上开了两个推理服务实例,而每个实例都初始化了一次 DeepEP,那显存占用会叠加得特别猛。

解决办法也很简单——要么显式控制进程只能绑定单卡,要么把单卡上的多个推理实例合并成一个进程,共享同一份 DeepEP Buffer。我在做推理服务的高并发部署时,最终选择了合并多路请求进一个进程的方案,单卡显存占用下降了 20% 以上。

5.4 其他值得注意的细节汇总

我把一些零散经验整理成表格,方便大家快速参考:

场景建议
跨节点通信优先在 RDMA 环境下使用,并确保网络拓扑无环路
路由分布倾斜配合负载均衡调度器或调整 Router 的 Top-K 策略
显存紧张调低 Buffer 的初始容量,或改用单进程多路复用的方式
多卡互联没开 NVLink 时优先级会降低,但不会阻断功能,注意显存带宽需要
与自定义训练框架集成时先做通信单测,确保输出张量与输入对得齐,再做全流程调优

6. DeepEP 适合哪些场景、不适合哪些场景

任何工具都不是万能的,DeepEP 也一样。我的判断如下。

6.1 适合大规模 MoE 训练

如果你的模型超过 100B 参数、使用 MoE 架构,并且打算在 32 张卡以上训练,那么 DeepEP 绝对值得你认真调研。这种规模下,通信时间是绝对的瓶颈,每一个百分点的通信优化都价值巨大。DeepEP 的预分配显存池和双模式设计,能有效支撑大规模训练场景的吞吐需求。

6.2 适合高并发 MoE 推理服务

做推理服务优化的朋友都知道,在请求潮汐波动很大的场景下,空闲等待是最可怕的浪费。DeepEP 的异步调度接口能把每个请求的通信任务打散到多条时间线上,让 GPU 一直处于忙碌状态。它的低延迟模式,也适合对首 Token 延迟有严格要求的对话式产品。

6.3 不适合小规模场景和极低延迟的单机场景

如果你的模型不大、只用一两张卡,DeepEP 的显存预分配机制可能显得有点“重”,动态申请的通用实现反而更灵活。而且单机场景下 NVLink 延迟原本就低,DeepEP 的相对优势不大。此外,如果你追求的是单请求微秒级的极致延迟,比如某些高频量化交易领域的模型推理,那通信库不是瓶颈核心,计算本身才是,这时候花精力调 DeepEP 的意义有限。

6.4 社区生态与后续展望

DeepEP 的价值不在于它本身代码量有多少,而在于它给 MoE 训练和推理提供了一个标准化的通信底座。在此之前,每一家做 MoE 推理的公司,基本都要自己写一套 All-to-All 的通信逻辑,而且写得千奇百怪,Bug 也千奇百怪。有了这样一个开源方案,后面的人至少不用从零开始了。

按照 DeepSeek 开源周主打“高密度技术输出”的节奏,后续可能还会放出更多与 MoE 推理相关的组件。如果它们能和现在的 DeepEP 形成完整链路,那对于整个开源生态来说,意义会更大。

7. 动手验证 DeepEP 的最小实验方案

如果你看完前面这些还是觉得有点抽象,那我建议你直接动手跑一个小实验。不需要很大的集群,两到四张卡就够,目标不是复现论文级别的数据,而是让你真实感受一下通信库的细节。

我的最小实验思路如下:

  1. 准备一个极小的 MoE 模型,比如 4 个专家、hidden_size 128,每个专家一个 Linear 层
  2. 在四张卡上分别部署一个专家,用 DeepEP 完成 Token 的分散和汇聚
  3. 跑一个简单的模拟训练 Step,打点记录通信耗时
  4. 换回 NCCL 的 AllToAll,重复同一个 Step,对比耗时

两步对比跑下来,你会非常直观地看到通信耗时差异。如果你体会不到差异,那大概率就是你的卡之间互联方式比较特殊,最后结果也不影响你理解 DeepEP 的工作机制。

我个人在实际操作中的体会是:通信库的选型和优化,往往比模型结构本身更影响最终的使用体验。很多团队在模型上花了很多心思调参,却在通信层用了一个最省事但不适合的方案,最后整体吞吐和延迟都很难看。DeepEP 这种专项优化的工具,至少给大家提供了一个正确的起点,真正怎么用好它,还是要结合你自己的集群拓扑、流量特征和显存预算来做针对性的配置。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 3:21:56

uniapp微信小程序插件选型、打包与排错实战指南

1. 三种插件形态,先搞清楚再动手1.1 uniapp里说的“插件”到底有几种开发做过一段时间后,几乎每个uniapp项目都会遇到“是不是该上插件了”的节点:地图组件表现不够好、扫码识别率上不去、支付流程需要更强的封装、音视频能力想要更底层一点的…

作者头像 李华
网站建设 2026/10/14 3:21:41

根据时戳从pytorch trace中获取kernel信息

选定区域需要使用https://ui.perfetto.dev/打开trace信息的json文件。获取选定区域的ts信息start_ts 6_758_429_328_000.000 end_ts 6_758_429_344_000.000 使用代码获取kernel信息 trace_pro.py import jsondef truncate_trace(input_file, output_csv, start_ts, end_ts,…

作者头像 李华
网站建设 2026/10/14 3:21:11

MATLAB TCP/IP网口调用:从tcpclient连接到字节序与粘包处理

简介:面向MATLAB用户的TCP/IP网口调用示例程序,主要解决远程设备数据通信、控制系统交互等场景下的网络编程需求,适合有一定MATLAB基础、希望快速上手网口通信的开发者和科研人员。压缩包内共含2个文件:1个可直接运行的.m脚本文件…

作者头像 李华
网站建设 2026/10/14 3:21:03

Faithfulness、Context Recall…RAGAS 核心指标怎么算(检索侧)(4/13)

总目录 [第 1 章] 你的 RAG 为什么总在「一本正经地胡说」 [第 2 章] 一张图看懂 RAGAS:核心概念与 7 大指标 [第 3 章] 数据构建——样本、数据集与模型接入 [第 4 章] Faithfulness、Context Recall…RAGAS 核心指标怎么算 [第 5 章] 生成侧指标与指标选型&#…

作者头像 李华