图 1:三种并行是三个正交的切分维度,乘起来就是集群规模。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料整理,与任何公司的当前招聘无关。训练千亿参数级模型,单卡既装不下也算不动,「3D 并行怎么组合」因此成了分布式训练面试的必问题。答好它的关键不是背三个名词,而是讲清每种并行各切哪个维度、各自的通信脾气,以及组合时谁先谁后。
第一种:数据并行(DP),切数据。
每张卡保存一份完整的模型副本,各算自己分到的不同 batch,反向传播后做一次梯度 all-reduce,保证所有副本的参数同步。实现最简单、扩展近乎线性,但显存效率最低——参数、梯度、优化器状态在每卡上完整冗余一份。通信发生在每步一次、量级是参数规模,频率低。
第二种:张量并行(TP),切单层内部。
2019 年的 Megatron-LM 论文系统化了这个方向:把一层里的矩阵乘按行或列拆到多卡,各卡算自己那一块,再在层内做 all-reduce 拼回结果。它是减少单层激活显存的手段,显存随并行度成比例下降;代价是通信最频繁——每一层都要通信,对带宽极度敏感,因此通常只敢放在同一台机器内部的高速互联上跑,跨机做 TP 利用率会掉到没法看。
第三种:流水线并行(PP),按层分段。
把模型按层切成几段,每段放一组卡,前一段的输出激活传给下一段。段间通信是点对点、每微批次只传一次激活值,通信量最小;代价是流水线有气泡——头尾阶段总有卡空等,靠把 batch 切成多个微批次流水填充来压气泡。
一句话对比:DP 通信最少但显存最浪费,TP 显存最省但通信最凶,PP 通信最省但调度最复杂。三者切的维度正交,所以可以叠加。
3D 并行:三个维度相乘,拼满整个集群。
总卡数 = DP × TP × PP。比如 512 卡的集群切成 DP 8 × PP 8 × TP 8:先按数据切成 8 个数据并行组,每组 64 卡各持完整模型;每组内再按层分 8 段流水线,每段 8 卡;每段内 8 卡做张量并行,各持有矩阵的 1/8。2021 年的 Megatron-LM 后续论文(arXiv:2104.04473)系统研究了三种并行的组合配比,在 3072 卡上把万亿参数模型训到每卡 52% 的峰值算力利用率,证明了这套组合能扩展到千卡规模。
用一段小脚本把切分账算清楚,Node.js 22.15.0 运行(node parallel3d.js):
function plan(total, dp, pp, tp, hidden, layers) {
if (dp * pp * tp !== total) throw new Error('维度乘积必须等于总卡数');
console.log(`${total} 卡 = DP ${dp} × PP ${pp} × TP ${tp}`);
console.log(`第一层切数据:${dp} 个数据并行组,每组 ${total / dp} 卡`);
console.log(`第二层切层数:每条流水线 ${pp} 段,每段 ${layers / pp} 层、${total / dp / pp} 卡`);
console.log(`第三层切张量:每卡持有矩阵的 1/${tp}(隐藏维 ${hidden} → 每卡 ${hidden / tp})`);
}
plan(512, 8, 8, 8, 8192, 64);
输出:
512 卡 = DP 8 × PP 8 × TP 8
第一层切数据:8 个数据并行组,每组 64 卡
第二层切层数:每条流水线 8 段,每段 8 层、8 卡
第三层切张量:每卡持有矩阵的 1/8(隐藏维 8192 → 每卡 1024)
部署口诀:通信越频繁的维度越靠内。
组合时的排布不是随意的:TP 每层都要 all-reduce,必须放在机内高速互联;PP 段间只传激活、量小,可以跨机;DP 每步才同步一次梯度,放最外层跨机也扛得住。口诀就是「通信越频繁的维度越靠内」。另外要提一句互补关系:ZeRO 系列解决的是「优化器状态、梯度、参数的冗余存储」,与 3D 并行的计算维度切分正交,实际大训练往往是 3D 并行加 ZeRO 一起上——ZeRO 的分阶段细节是另一道题,这里不展开。
图 2:512 卡按 DP→PP→TP 逐层切分,每层各解决一个瓶颈。
面试答题骨架:先讲单卡装不下、算不动这两个动机,再按「切什么、通信特点、代价」三栏过一遍 DP/TP/PP,然后给出总卡数公式和一个具体切分例子,最后落到「通信越频繁越靠内」的部署口诀和与 ZeRO 的互补关系。能把「为什么 TP 不能跨机」讲清楚,基本就站稳了。你在实际训练里调过并行配比吗?气泡和通信哪个更头疼?欢迎聊聊。