1. 首页
  2. 精选文章
  3. DeepSeek-V4 的并行策略和计算通信遮掩

DeepSeek-V4 的并行策略和计算通信遮掩

  • 发布于 2026-05-05
  • 0 次阅读

作者:九老师
https://zhuanlan.zhihu.com/p/2034669267807888405

最近模型从Dense切到了MoE,MFU 也相应地暴跌了,大家直觉上觉得Expert被切的很小,所以计算强度上不去,但实际切分完的维度至少也有1024,MFU暴跌的原因一定不来自这里。

深入理解这个问题,就是理解GPU的分布式并行计算,要在计算和访存bound之外,引入通信bound,而解决吞吐和MFU的问题的手段,就是设计合理的GPU并行策略,做好GPU计算和通信的遮掩(overlap)。

DeepSeek 的 H800 和昇腾卡,8 卡 nvlink 高速互联,跨节点都是 IB(InfiniBand)低速网络,我们手里虽然有B200,但实际也也没用上 NVL72,所以DeepSeek 的并行策略有普适的借鉴意义——硬件基础相似,低成本方案,新的 MoE 的方案也做了开源。

DeepSeek 的并行策略

V4重点讲了EP策略的计算和通信遮掩,没有细讲他的并行策略,它是延续DeepSeek-V3 的。

Our training framework is built upon the scalable and efficient infrastructure developed for DeepSeek-V3.In training DeepSeek-V4, we inherit this robust foundation

V3披露的硬件是2048张H800,8卡节点内 nvlink互联,并行策略是16-way PP×64-way EP×ZeRO-1 DP。

这里先说下nvlink和IB的差异,nvlink下的8卡内速度可以到900GB/s,而节点间的IB速度为50 GB/s。EP跨节点占用了IB 带宽,这里是主要的通信bound,这里的每一份通信安排都至关重要,这也造就了 DeepSeek 的精妙的并行策略选择。

在展开更多细节之前,先搞清楚什么是GPU并行策略?

最朴素的并行策略就是DP(Data Parallel,数据并行),每一张卡保存了全部参数,读不同的数据,做不同的计算,最后合并梯度做更新。在模型参数不大的年代里,这是最自然的并行方式。

模型开始变大,GPU的显存装不下了,就要考虑怎么拆分存储,计算用的 inputs 和 weights不在一块 GPU 上,那么就要进行通信。

多大的模型就参数,就要考虑做拆分了?几百B的模型自然装不进一张 H100(80B),那 10B 的模型呢?这就是一个经典问题,训练时模型的显存占用有多大。

显存里要放哪些东西?答案是参数/梯度/优化器状态/激活值。

一个10B的模型,在FP16混合精度下,参数是2字节,那么就是20GB的存储;而梯度也是FP16,那么又是20GB的存储;优化器的状态就复杂了,以 经典AdamW为例子,两个一阶矩和二阶矩都要用FP32存,这就要2x4x10=80GB了,再加一个Master Weights,以FP32存储,那就是 4x10=40GB。

所以,在不考虑任何激活值和系统占用的情况下,光是参数/梯度/优化器就 10x16=160GB 了。

再考虑上激活值,一个模型3~5B就要思考它在 朴素DP之外的分布式并行策略了。

几种常见的策略:

DP基础上,计算不变,把存储拆分到多个 GPU 卡上,每卡只存一部分。如果拆分的是 12 倍的优化器状态,那么这就是 ZeRO-1(Zero Redundancy Optimizer);把梯度也做了拆分,这就是 ZeRO-2;如果把模型参数则做了拆分,那这就是 ZeRO-3,也叫 FSDP(Fully Sharded Data Parallel)。

TP(Tensor Parallel),是按参数的Tensor 做切分,放在不同的 GPU 上存储和计算。这里经常出现的混淆是,FSDP 也切分了参数,它和 DP 有什么区别?

答案是FSDP只是切分了存储,没有切分计算,它本身还是 DP,需要做前向计算的时候先把其他 GPU 上的参数 shard 拉回来,组成完整的 Tensor,和朴素 DP 一样做前向的计算,而 TP 则是同时也拆分了计算,不同的 GPU 读的是相同的数据,做了不同 shard Tensor 的计算,再通信合并计算结果。

TP的特点是通信量大,适合放在 nvlink 域内。

PP(Pipeline Parallel),它是最好理解的,Transformer 的不同的 Layers 拆到一组 GPU 上做计算,多组 GPU 就形成了一条流水线,这就自然地按层拆分了存储和计算。它的通信量小,适合跨节点。

EP(Expert Parallel),它是 MoE 模型的标配。它实际和 TP 有一点像(都是参数拆分存储和计算),Expert 是天然拆分的,放在不同的 GPU 上存储参数,在计算时,token 路由到对应的 GPU 完成Expert 的计算,然后再把结果 Combine 回来。通信量也重,DeepSeek 的关键优化就是怎么能在 IB 网络上把 EP=64 跑明白。

CP(Context Parallel),在Attention 内部沿 seq 切激活,每张卡算一段的 OKVO,Attention 的时候做跨 GPU 通信。V3 中没有提到,V4 中1M 上下文装不下,做了 sequence 维度的切分,一般出现在预训练的 long context 阶段或 post-train 中。

多种并行策略可以叠加吗?

如果你有 N 块 GPU,上面的各种并行策略是正交的,他们可以进行叠加,比如基础的 3D 并行是 DP x TP x PP,并且是 DP x TP x PP = N。

EP特殊,它可以寄生在 DP 之上,就是可以共用卡。比如一个 5D 并行:N=DP_total x TP x PP x CP, 其中 DP_total = EP x DP_replica, DP_replica代表模型权重完全复制的组数。

再看下 DeepSeek 的例子,2048 张 GPU,PP = 16,EP = 64,实际 DP_total = 128,DP_replica = 2. 就是模型实际就 2 个完整副本,内部做 EP,GBS(global batch size) = MBS(micro-batch size) x DP_totalxgrad_accum_steps。

EP 能和 DP 共用卡,是个特例。EP 是唯一一个”在 forward pass 内部把 token 重新换主”的并行维度——它通过 all-to-all 让 token 临时离开自己的 DP rank、被另一组卡处理、再送回来。

假如教室是 GPU,每个学生(数据)有自己的教室,每个班主任(Expert)有自己的教室,它同时作为选修课老师给所有教室的学生上课,那就是上课的时候让学生重组下教室,下课的时候他们再回去。

TP 为什不行,它实际要求所有老师合上一节课,又不能离开自己的教室,学生必须要分身去上课了;EP 可以是因为它的MoE稀疏性和 Expert 的独立性。

先让 EP 在 IB 网络上跑起来

EP 策略是包括dispatch,Expert计算,Combine 多个串行流程,dispatch 是把 token 通过 all-to-all 给到所在专家的 GPU 上,完成计算后,Combine把结果用 all-to-all 传回来。

为了使得节点间通信不爆炸,它在做专家选取的时候,限制只能在 4 个平均最优的节点内选 8 个专家,使得跨节点通信次数减少,但这并不是最优的专家选择,而在 V4 中则取消了这个限制。

为什么这么做可以限制 IB 上的通信量呢?如果 top-k=8,然后这 8 个专家又恰好在 8 个不同的节点里,那么所以的通信走的都是IB 网络。

限制了最多 4 个节点,那么平均每个节点要路由到 2 个 GPU,它可以在 IB 网络上归并传输的信息,先交给目标节点里的一块 GPU,再由它通过 NVlink 转发到对应的GPU,把 IB通信量转成 NVlink 通信量,就是降低了通信的时长,这里争取的通信时长越低,后续 EP 的计算通信遮掩就变得越容易。

DP ZeRO-1 和 EP/PP 的组合

因为 Transformer 是天然分层的,DP 天然计算和通信可遮掩。ZeRO 本质是切参数,计算都发生在当前 GPU 上,这是天然可遮掩的条件。

DP,我在反向传播的时候,计算下一层的时候,上一层可以把刚才的梯度做all-reduce。

ZeRO-1/2,因为优化器和梯度是分片的,做reduce-scatter把自己梯度 shard 做更新,然后更新优化器状态,权重更新后做一次 all-gather更新权重。

ZeRO-3/FSDP,是每次前向都做一个 all-gather,但可以再上一层算的时候,all-gather 拉下一层的参数。

DP 选择了 ZeRO-1,而非 FSDP/ZeRO-3 或 ZeRO-2,这背后的选择是避免和EP 的通信墙 IB 带宽。首先 ZeRO-3 每次前向都要all-gather 取到权重参数,一次 micro-batch 的通信量就是参数量(GB 级别),这和 EP 的 forward 直接形成了 IB 带宽抢占。

ZeRO-2 GPU只保留部分梯度,那梯度累积的时候就不能像 ZeRO-1 那样只是本 GPU 累加,而是要reduce-scatter通信,通信量也是参数量(GB 级别),和 EP 的 backward 抢占 IB 带宽。

而 ZeRO-1,在非优化器step 是没有通信的,这和DDP 一样;优化器步骤做更新只能更新本 GPU 的参数,因而需要做一次 all-gather 才能拿到正确的参数。

由于 DP 和 ZeRO 本来就要在优化器步做一次all-reduce把梯度累加,all-reduce 可以拆成reduce-scatter和all-gather,那在reduce-scatter后每一个shard 优化器对应的梯度都是对的了,把完成参数更新后,再 all-gather 一次参数即可,那么原本梯度的 all-gather 替换成了更新后的参数 all-gather,ZeRO-1 的通信量和普通 DP 一模一样,却省了接近12倍参数的显存,这就是 ZeRO-1 是近乎免费的原因。

DeepSeek 选择把宝贵的 IB 带宽留给了 EP 的通信。

EP 的 all-to-all 操作, 通信量是 B x L x seq_len x top-k x hidden_size x 4。其中 forward 和 backward 分别有两次 all-to-all。这个几乎把 IB 通信占住了,通信量打到了 IB 带宽的同量级。

选 PP 也很自然,H800 是 8 卡互联,跨节点通信速度从 900GB/s 的 nvlink 降低到 50GB/s 的 InfiniBand,PP 的通信量小(B x seq_len x hidden_size x 2),适合跨节点,也避免了抢IB 带宽。

综合看,PP+EP+DP ZeRO-1,没用 TP,没有 ZeRO-2/3,避开了在每一个 micro-batch 上去抢占EP 的 宝贵 IB带宽,使得整体的并行策略能够在低配硬件下顺利进行。

PP 的气泡挤压

PP 的通信量很小,所以不存在遮掩的问题,它是因为要等反向计算而产生的流水线气泡。上图为最朴素的 PP——GPipe,这个方案已经废弃了,只是为了说明气泡问题。

1F1B(1 Forward 1 Backward) 的策略是在做完 F0,直接接一个 B0,开始交替一次 forward 一次 backward,实际并不能减少气泡,但是由于激活可以更早丢掉,那么显存占用更小,可以增大 micro-batch数M变相增大了计算比例,挤压了气泡。

GPipe 和 1F1B 的气泡占比都是 (P-1) / (M+P-1),P 是 PP 数,M 是一个 step 的 micro-batch 数。

ZB1P(Zero Bubble PP)的解决方案是在 1F1B 基础上,把每一层反向计算做一个拆解,反向计算是根据 dy 来计算 dx 和 dw 的过程,前一层之所以不能进行计算是依赖了 dx,那么先把 dx 计算出来,往回挤压气泡,把 dw 的计算留到气泡产生时发生。

但也因为要延迟计算 dW = x^T · dy,它的底料x 也就是激活值不能立即释放,相当于用一些显存的占用换了计算。

DualPipe:挤压气泡同时搞定 EP 的通信计算遮掩

DeepSeek-V3 就引入了 DualPipe 方案,它是在ZB1P基础上,在 PP 上形成两条反向的 micro-batch,使得 Forward 和 Backward 恰好把计算和通信错开,挤压气泡的同时把 EP 的通信遮掩到了计算背后。

EP 的计算和通信的流程是串行的,forward 的时候通过一次 all-to-all 把 token 的 hidden 路由到对应专家的 GPU(dispatch),完成计算,然后把结果用一次 all-to-all 再发回原GPU(combine),也是天然不可遮掩。

EP 吃了最大头的 IB带宽, DeepSeek V3 和 V4 都在讲它的通信和计算时间接近 1:1,能不能把通信遮掩到计算背后就成了关键。

每一个 rank 持有两份参数,也就是 micro-batch 可以从 rank0 留到 rank3,反之也可以从 rank3 流到 rank0. 每一个 step 开始,双向做 forward,当双向第一个 Backward 到来后,它与逆向的 forward 发生了交叠。

交叠处,一个正向[attention, dispatch, MLP, combine]与一个逆向的发生了通信和计算的遮盖。DualPipe 的副作用就是显存占用增加了一倍,PP+EP+ZeRO-1 已经把显存占用切到很小了,V3 paper 说这可以接受。

具体的实现上,H800 共 132 个 SM,划出 20 个 SM 专门做通信,剩下的给计算。传统 NCCL 的工作模式是 SM-shared:通信 kernel 和计算 kernel 共享 SM 池,每个 kernel 启动时按需占用一些 SM。这 20 个 SM 跑的是 DeepSeek 自己写的 PTX 级通信 kernel,不是 NCCL。

V4 的 Waved-EP 通信计算遮掩

Waved-EP是一个DeepSeek-V4 的新的 EP 计算通信遮掩方案,一个 kernel 直接在 MoE 层解决问题,不再依赖 PP 调度了,它思路很简单:直接把 Expert 切成几个 wave,在不同 wave 之间做通信遮掩。

既然DualPipe 已经能够遮掩掉EP 阶段的 all-to-all 通信,那为什么还要做Waved-EP的新 kernel 呢?论文给的答案是,在小 batch 的RL 或推理情况下,DualPipe 就不够用了。

原理上推理就没有 Backward,那也就无法和 Forward 做相互一一对应的通信遮掩了。所以对比baseline(Comet),RL 场景下1.96x加速,通用场景下1.50 ~ 1.73×。

当然,论文并没有对比 DualPipe 的遮掩,因为V3 也是 100% 遮掩,大 batch 场景下应该长不多。

为什么 DualPipe 依赖大 Batch?PP 在 1F1B 基础上做交叠,可以大致分成 3 个阶段:warmup全 F,没有 B 配对;稳态F+B overlap;cooldown是纯 B/W 排空。warmup 阶段只有 F,cooldown 阶段只有 B,那就无从遮掩通信,F和F/B和B 交叠下,通信和计算没法交错,也无法遮掩。

DualPipe 的Forward 和 Backward 遮掩,就发生在稳态阶段,稳态的长度依赖于 M(micro-batch 的数量),batch size=M x micro-batch x DP。所以RL或 Long Context这种小 batch 下,M 过小,稳态过短,遮掩的效果就大打折扣。

DeepSeek V4 的方法,是一个 kernel 直接在 MoE 层解决问题,不再依赖 PP 调度了。

它是把 Expert 分成几组,每组一个 wave,dispatch 确保第一个 wave 通信完成,就开启计算,同时第二个 wave的 dispatch 也在进行,算完了就直接 Combine,每一个 wave 内串行,但 wave 间的 dispatch/L1+Act+L2 计算/Combine 就可以并行起来了。

他是 kernel 级别的方案,那么就无所谓训练还是推理,大 Batch 还是小 Batch,更多的依赖来自于细粒度专家的模型设计,wave 过少第一个dispatch 和最后一个 Combine 暴露无法遮掩。

这个 kernel 是用 TileLang 框架完成的,这么精细化的通信和计算的控制,triton 是很难完成的。以下是 Claude 的评价:

Triton 不是为通信和计算融合设计的,它的核心抽象是「单 GPU 内部的高性能 GEMM/element-wise kernel」,对跨 GPU 通信原语(nvshmem、IB send/recv)支持很差或没有。Waved-EP 这种 mega-kernel 要把 dispatch (NVSHMEM put/get) + GEMM + combine 融在一个 kernel 里,Triton 根本写不出来。

TileLang 的优势不只是「更容易控制通信遮掩」,而是 它能写出 Triton 写不出来的 kernel。这是质的差别,不是程度差别。

TileLang 是 V4 论文 §3.2 自己介绍的工具(论文引用 Wang et al., 2026)。它的设计哲学跟 Triton 不一样 —— 既要 Triton 那样的高生产力 DSL,也要保留 PTX/CUDA 级的底层控制权

DeepSeek 会通过极致的并行策略设计和 kernel 级别的通信遮掩,8 卡节点+IB 网络也能玩转 1T 以上模型,甚至建议厂商不要一味地堆通信带宽,而是看GPU 在计算和通信遮掩时的总功率是否能支撑,导向是硬件对计算和通信并行做更好地支撑。

而Nvdia 的发展路径就是通过 NVlink 连接更多的卡,GB200 是 72 卡互联,明年 Rubin 的 Ultra 是NVL576。让你 GPU 并行时更容易搞出来 5D 并行,带宽足够快,把 N 块 GPU 当成一个整体来用。

我也很感慨,上次看到评论区有人瞧不上 LLM 的“力大砖飞”,实际上我觉得在这种极致地工程能力下,在计算和通信上的平衡堪称艺术,比那种模型结构的奇技淫巧要高牛的多。

还有一些没写到的

  • 引入 muon 后,ZeRO-1 没法切元素了,如何按矩阵做拆分。
  • DualPipe-V,虽然 DeepSeek-V4 没明确引入,这个方法可以把 DualPipe 冗余的参数去掉,把激活值恢复到 1F1B 的 1 倍量。
  • CP ,怎么和 Attention 压缩配合

reference

1.https://arxiv.org/pdf/2512.24880
2.https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro/blob/main/DeepSeek\_V4.pdf
3.https://arxiv.org/pdf/2412.1943