2026-04-22 · architecture / ai-infra
【大模型基础设施工程】07:Megatron-LM 与 DeepSpeed
开源训练框架双雄对比,覆盖 Megatron-LM、DeepSpeed、FSDP2、torchtitan、Colossal-AI,含选型与工程实操。

























单卡 80GB HBM 最多装 70B FP16 的参数(不含梯度、优化器、激活)。 要训 DeepSeek-V3 这样的 671B MoE,必须把模型切成几千片,让几千张 GPU 像一台机器一样工作。 切分的艺术,就是并行的艺术。
| 年份 | 事件 | 规模/突破 |
|---|---|---|
| 2017 | Transformer 原论文 | 65M 参数 |
| 2018 | BERT-Large | 340M |
| 2019 | Megatron-LM 发布 | 8B,奠定 TP |
| 2019 | ZeRO-1/2/3 | DeepSpeed,DP 极致省显存 |
| 2020 | GPT-3 175B | PTD-P 3D 并行 |
| 2022 | Megatron-Turing NLG 530B、BLOOM 176B | 多集群合训 |
| 2023 | GPT-4(推测 MoE)、Llama 2 70B | MoE 工程化 |
| 2024 | DeepSeek-V3 671B MoE + DualPipe | 零气泡 + FP8 |
| 2025 | Llama 3 405B、Qwen 2.5、GLM-4.5 | 10T+ tokens 成常态 |
每一次规模跨越都伴随并行策略的一次补刀。
训练一个 Transformer,GPU 显存的消耗大体分为四块:
| 组成 | 大小(以参数 N 为单位) | 说明 |
|---|---|---|
| 参数(Weights) | 2N(FP16) | 前向、反向都要用 |
| 梯度(Gradients) | 2N(FP16) | 反向累积 |
| 优化器状态(Optimizer) | 12N(Adam FP32 m+v+master) | 混合精度下的主拷贝 |
| 激活(Activations) | 与 batch、seq、depth 成正比 | 反向 recompute 时需要 |
以 Adam 混合精度训 70B 为例:参数 + 梯度 + 优化器 ≈
2+2+12 = 16 × 70B = 1120 GB,再加激活——单卡
80GB 连塞一角都不够。
除了显存,还有两堵看不见的墙:
所以并行化的目标不是”把模型切开能装下”就完事,而是在切分带来的通信代价和计算能完成的比例(MFU)之间找平衡。工业界 Pre-train 的 MFU 目标:密集模型 40%+,MoE 30%+,能稳到 50% 算极优。
后面所有讨论都离不开这四种 NCCL 原语,先一次性说清楚:
AllReduce(x_i) → 每个 rank 拿到 Σ x_i ;流量 ≈ 2(N-1)/N × |x|
ReduceScatter(x_i) → 每个 rank 拿到 Σ x_i 的 1/P 片;流量 ≈ (N-1)/N × |x|
AllGather(s_i) → 每个 rank 拿到全部 s_i 拼接 ;流量 ≈ (N-1)/N × |s|×P
All-to-All(x_i[j]) → rank i 的第 j 片发给 rank j ;流量 ≈ (N-1)/N × |x|
恒等式:AllReduce = ReduceScatter + AllGather,流量减半——这正是
ZeRO-2/3 与 SP 的魔法来源。
最朴素:每张卡完整持有一份模型副本,把 global batch 切成 N 份,每卡前向 / 反向各一份,反向后对梯度做 AllReduce。
GPU0: model | batch[0:B/N] --\
GPU1: model | batch[B/N:2B/N] --> AllReduce(grad) --> update
GPU2: model | batch[2B/N:..] --/
2N 字节(FP16
参数规模的梯度)× AllReduce 因子
2(N-1)/N ≈ 2。把单个大矩阵乘法切到多卡上,由 Megatron-LM 定型。切法两种:
Y = XA,把 A
按列切 [A1, A2],每卡输出
Yi = X Ai,最后需要 AllGather
合并 Y。Y = XA,把 A
按行切,同时 X 也按列切,每卡算
Yi = Xi Ai,最后 AllReduce
求和。Megatron 的 MLP(Linear → GELU → Linear)采用”列切 + 行切”配对:
X --[col-split Linear]--> Y(split) --GELU--> Z(split) --[row-split Linear]--> AllReduce --> Out
Attention 的 QKV Linear 是列切(切 head 维度),Output Linear 是行切。一层 Transformer 有 2 次 AllReduce(forward)+ 2 次 AllReduce(backward,与 SP 结合可以换成 ReduceScatter/AllGather)。
4 × batch × seq × hidden
字节(FP16)。按层切,不同 stage 放到不同 GPU 上。前向像流水一样:GPU0 算完第 1-8 层,把激活送到 GPU1 算 9-16 层……反向反着走。
经典问题是”气泡(bubble)“——头尾阶段的 GPU 必然有空闲。后文 §五 专门讲。
通信:stage 之间只发送 / 接收激活与激活梯度,是 P2P send/recv,数据量远小于 DP 的 AllReduce,跨节点友好。
当 seq_len 达到 32K、128K 乃至
1M,激活(B × S × H)本身就爆显存,单
TP 切不动了。SP 沿 序列维度 切分:
ReduceScatter + AllGather
等价变换,峰值激活降 TP 倍。(B, S/P, H) 换成
(B, S, H/P),走完 attention 再换回来。head
数要能被并行度整除。实操上:Ulysses 在 TP 已经切了 head 的情况下不好叠加,Ring/CP 正交性更好;DeepSeek-V3 和训练 128K+ 上下文的主流是 CP + TP + PP + EP + DP。
MoE 每层有几十到几百个 expert,但 token 只路由到 top-k 个。EP 做的就是把 expert 分散到 N 张卡:
token routing -> all-to-all dispatch -> local experts -> all-to-all combine
五个维度的关系可以用一张图表达:
DP 的痛点是”每张卡都完整保存参数 / 梯度 / 优化器”,冗余极大。ZeRO(Zero Redundancy Optimizer,DeepSpeed 2019)把这三份状态切分到 DP 各卡上。
| 阶段 | 切分内容 | 单卡状态显存 | 额外通信 |
|---|---|---|---|
| ZeRO-1 | 优化器状态 | 2N + 2N + 12N/Ndp | 无(只在优化器 step 时处理) |
| ZeRO-2 | +梯度 | 2N + 2N/Ndp + 12N/Ndp | ReduceScatter 替 AllReduce |
| ZeRO-3 | +参数 | (2N + 2N + 12N)/Ndp | 前向 / 反向前 AllGather 参数 |
ZeRO-3 的通信量 ≈ 标准 DDP 的 1.5 倍(AllGather +
ReduceScatter + AllGather 反向),但显存降到
1/Ndp。
PyTorch 原生实现的 ZeRO-3:
import torch
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
from torch.distributed.fsdp import ShardingStrategy
model = FSDP(
model,
sharding_strategy=ShardingStrategy.FULL_SHARD, # ZeRO-3
mixed_precision=mp_policy,
device_id=torch.cuda.current_device(),
)关键细节:
DTensor,取消 FlatParameter,与 TP
/ PP / TorchTitan 组合更干净。工业预训练一般不用 Offload,SFT / LoRA 经常用。
ZeRO 家族里还有常被忽视的一档:
--sequence-parallel 等价。这些在纯 DeepSpeed 栈里是 ZeRO-R;Megatron / FSDP 都内化实现了类似机制。
| 维度 | FSDP2 | DeepSpeed ZeRO-3 |
|---|---|---|
| 代码集成 | 纯 PyTorch,改动小 | 需要 deepspeed engine 包一层 |
与 torch.compile |
逐步完善 | 兼容有限 |
| CPU/NVMe Offload | 有限 | 成熟(Infinity) |
| MoE 支持 | 需配合 Expert Parallel 手写 | 集成 MoE + EP |
| Checkpoint | DCP(Distributed Checkpoint) | 内置切片 + 合并工具 |
| 生态 | TorchTitan / HF Accelerate | HF Trainer / colossal / DS-Chat |
选型不是二选一:国内头部团队常”Megatron 主干 + DeepSpeed 的优化器/Offload + FSDP2 做 SFT/RL”混用。
单用任何一种都不够,工业训练都是 TP × PP × DP (× SP × EP) 的笛卡儿积。
| 场景 | 规模 | TP | PP | DP | SP/CP | EP |
|---|---|---|---|---|---|---|
| Megatron 175B(8×A100 node×多节点) | 175B dense | 8 | 8 | 128 | 1 | - |
| Llama 3 70B(官方) | 70B dense | 8 | 4 | - | 1 | - |
| DeepSeek-V3 Pre-train | 671B MoE | 1(无 TP) | 16 | 16 | - | 64 |
| 长上下文 SFT(128K) | 13B | 2 | 2 | 32 | 8 (CP) | - |
DeepSeek-V3 的一个设计亮点:放弃 TP,用 EP + PP + ZeRO-1 组合,因为 MoE 的 FFN 已被 expert 天然切分,不再需要 TP 切 MLP;这样避免了 TP 带来的每层 AllReduce,换来更大的 PP 窗口。
以 TP=8, PP=4, DP=16 共 512 卡为例,NCCL
里会创建三类 ProcessGroup:
rank = tp_rank + tp_size * (dp_rank + dp_size * pp_rank)
TP group:同一 (pp_rank, dp_rank) 的 8 张卡 → 放 node 内 NVLink
PP group:同一 (tp_rank, dp_rank) 的 4 张卡 → 跨节点,但通信量小
DP group:同一 (tp_rank, pp_rank) 的 16 张卡 → 跨节点 AllReduce
Rail-optimized 拓扑:ibN
网卡与 GPUn 一一配对,DP AllReduce 走自己的
rail,避免 cross-rail。这是 NCCL 2.18+
NCCL_CROSS_NIC=0 的默认行为。
记:a = batch × seq × hidden,L
层,N 参数。
| 维度 | 数据量 | 说明 |
|---|---|---|
| DP AllReduce | 2N(FP16) | 一次 |
| TP AllReduce | 4L × a | 每层 4 次(fwd 2 + bwd 2) |
| PP P2P | 2L/PP × a | stage 边界激活 + 梯度 |
| SP(与 TP 合并) | 4L × a(总量不变,换成 RS/AG) | 峰值激活 ↓ TP 倍 |
| EP all-to-all | 2 × topk × a × (1−1/EP) | MoE 层才算 |
经验规则:若 DP AllReduce 超过 20% 单步时间,考虑升 ZeRO 或砍 DP;若 TP AllReduce 跑出节点,直接掉 MFU 30%+。
以 TP=4, PP=2, DP=2,共 16 GPU 为例:
PP stage 0 PP stage 1
┌──────────────┐ ┌──────────────┐
DP group 0 │ R0 R1 R2 R3 │──PP──▶│ R8 R9 R10 R11│
│ ◀── TP ────▶ │ │ ◀── TP ────▶ │
└──────┬───────┘ └──────┬───────┘
│ DP AllReduce │ DP AllReduce
┌──────┴───────┐ ┌──────┴───────┐
DP group 1 │ R4 R5 R6 R7 │──PP──▶│R12 R13 R14 R15│
└──────────────┘ └──────────────┘
{0,1,2,3}、{4,5,6,7}、{8,9,10,11}、{12,13,14,15}。{0,8}、{1,9}、…、{7,15}。{0,4}、{1,5}、…、{11,15}。Megatron 按 tp × dp × pp 的顺序把 rank
扁平化,这也是”TP 放同 node”自然成立的原因——同一个 node 上的
8 个 rank 天然落入同一 TP 组。
设 PP 度 P,micro-batch 数
M:
(P-1) / M。若 P=8, M=16,气泡
47%!(P-1) / M,但峰值激活显存降为 P 份而非
M 份。v 个非相邻层段,气泡 =
(P-1) / (v × M),代价是 P2P 次数 ×v。F / B / W 让气泡降到理论 0。gantt
title 1F1B vs Zero Bubble vs DualPipe(P=4,单位 = micro-batch 步)
dateFormat X
axisFormat %s
section 1F1B
GPU0 F1 :a1, 0, 1
GPU0 F2 :a2, after a1, 1
GPU0 F3 :a3, after a2, 1
GPU0 F4 :a4, after a3, 1
GPU0 B1 :b1, after a4, 1
GPU0 B2 :b2, after b1, 1
GPU0 B3 :b3, after b2, 1
GPU0 B4 :b4, after b3, 1
section ZeroBubble
GPU0 F1 :c1, 0, 1
GPU0 F2 :c2, after c1, 1
GPU0 B1 :c3, after c2, 1
GPU0 W1 :c4, after c3, 1
GPU0 F3 :c5, after c4, 1
GPU0 B2 :c6, after c5, 1
section DualPipe
GPU0 F1+ :d1, 0, 1
GPU0 F2+B1- :d2, after d1, 1
GPU0 F3+B2- :d3, after d2, 1
GPU0 F4+B3- :d4, after d3, 1
GPU0 B4- :d5, after d4, 1
B / W 拆分反向传播中:
B(grad_input = grad_output @ W.T):下游需要
→ 必须尽早算。W(grad_weight = X.T @ grad_output):只自己用
→ 可以延后到流水尾巴。把 W 往后推,中间空档让其他 micro-batch 的
F/B 填进来。
DualPipe 核心思想:
ATTN → MLP → combine → dispatch
重排,让反向计算遮蔽前向 all-to-all。代价:显存需要保存 2 份参数副本(因为两个方向同时在算),适合 MoE 这种参数相对 sparse 的结构;Dense 大模型用 DualPipe 不划算。
MFU 上 50% 的关键:让每一次集合通信都藏到 GEMM 后面。
# PyTorch 内部约定:通信走单独的 NCCL stream
comm_stream = torch.cuda.Stream()
with torch.cuda.stream(comm_stream):
dist.all_reduce(grad, async_op=True)
# 计算继续在默认 streamDDP 的 bucket 机制:梯度按桶(默认
25MB)触发 AllReduce,反向还没结束、前面桶的 AllReduce
已经在跑。
--sequence-parallel + FlashAttention 2.x +
cuBLASLt):把 AllGather 拆 chunk 与 MatMul pipeline
重叠,近年的”tensor parallelism with sequence parallel
communication overlap”即此。MoE 的 all-to-all 最痛苦。DualPipe 把
dispatch / combine 拆成 4 个 warp group,与本
chunk 的反向 compute 用不同 SM:
Compute SMs: | attn_bwd | mlp_bwd |
Comm SMs: | dispatch(next) / combine(prev) |
这是 DeepSeek-V3 能在 H800(NVLink 带宽被砍一半)上做到 MFU 40%+ 的秘密武器。
反向用到的中间激活占用极大(B × S × H × L × 12
级别)。checkpoint:前向时只存 block
入口激活,反向时重新跑一次前向。换显存与 1/3 额外算力。
for step, batch in enumerate(loader):
loss = model(batch) / accum_steps
loss.backward() # 累在 .grad 上
if (step + 1) % accum_steps == 0:
optimizer.step()
optimizer.zero_grad()梯度累积把有效 batch size
放大而不增显存,但会降低 PP / DP AllReduce 的
overlap
机会(因为最后一次累积后必须同步)。Megatron
的做法是把
global_batch = DP × micro_batch × accum,accum
等于 PP micro-batch 数。
一个 Transformer Block,FlashAttention 2/3 下保存的激活量大约是:
act ≈ B × S × H × (34 / TP) bytes(BF16,含 norm / residual / proj 输出)
L 层全保留:L × B × S × H × 34 / TP。以
L=96, B=2, S=4096, H=12288, TP=8 估算:
96 × 2 × 4096 × 12288 × 34 / 8 ≈ 41 GB / micro-batch,1F1B
再 × P = 几百 GB,不开 recompute 铁定 OOM。
开 selective recompute 后约
10 / TP,激活减到
12GB/micro-batch,完全可控。
对超长序列(256K+),就算 recompute 仍然很痛。可以把 block 前向输出 offload 到 CPU,反向时异步拷回:
act = act.to("cpu", non_blocking=True) # 前向尾部
... # 其他 block 继续算
act = act.to("cuda", non_blocking=True) # 反向前预取PyTorch 的 torch.utils.checkpoint +
offload_to_cpu=True、Megatron 的
--cpu-offloading 自动处理。代价是 PCIe
带宽(64GB/s for Gen5 ×16)会成为新瓶颈,典型折算 MFU 降
5–10%。
MoE 训练最头疼:某些 expert 成”网红”,其他 expert 饥饿。解决:
cf × avg_tokens,超出 drop。每层 TFLOPS 不同:embedding、LayerNorm、FinalHead 比中间层轻。静态均分会让 stage0、stage_last 变慢成木桶短板。
max(sum_stage)。Megatron 的
--pipeline-model-parallel-split-rank
支持自定义。长短样本混批时,seq=4K 和 seq=128K 同 batch 等于短样本陪跑。做法:
tokens 而非 samples 定义
batch。给每一层一个 profile 耗时 t[i],要切成
P 段使 max(Σt) 最小:
def split_pp(t, P):
L = len(t)
# dp[i][p]: 前 i 层切 p 段的最小瓶颈耗时
INF = float("inf")
dp = [[INF] * (P + 1) for _ in range(L + 1)]
cut = [[0] * (P + 1) for _ in range(L + 1)]
dp[0][0] = 0
prefix = [0]
for x in t:
prefix.append(prefix[-1] + x)
for i in range(1, L + 1):
for p in range(1, P + 1):
for j in range(p - 1, i):
seg = prefix[i] - prefix[j]
val = max(dp[j][p - 1], seg)
if val < dp[i][p]:
dp[i][p] = val
cut[i][p] = j
# 回溯 cut 得到每 stage 的层区间
return dp[L][P], cut生产上(Megatron-Core
PipelinePartitioner,Colossal-AI
PipelineGPTPolicy)用这种思路自动均衡。
NVIDIA Transformer Engine(TE)把 GEMM 输入做动态 scaling 转到 FP8(E4M3 / E5M2),AllReduce 本身一般仍走 BF16 / FP16(Hopper 的 NCCL 现已支持 FP8 collective,但数值稳定性需谨慎)。
DeepSeek-V3 自研了一套 E4M3-only 的 FP8 混合精度,对 GEMM 使用 FP8,对累加 / 权重更新保持 FP32,在 H800 上把算力吃干。
O(log N),大规模 DP AllReduce 默认。Megatron 启动脚本常见:
export NCCL_IB_HCA=mlx5
export NCCL_IB_GID_INDEX=3
export NCCL_SOCKET_IFNAME=eth0
export NCCL_ALGO=Tree
export NCCL_CROSS_NIC=0
export NCCL_IB_SL=1真显存不够时的兜底。一般组合:
transformer_engine 集成 FP8。ParallelConfig,支持 Mamba 等 hybrid
arch。torch.compile 搭配越来越丝滑。DeepSeek 在 v3 后开源了 DualPipe
调度器(github.com/deepseek-ai/DualPipe),以及
EPLB(Expert Parallelism Load Balancer)和
profile-data。对做 MoE
预训练的团队是一手材料。
下面给出一个可运行骨架(需要 PyTorch 2.4+,实际训练请结合 TorchTitan)。
import os
import torch
import torch.nn as nn
import torch.distributed as dist
from torch.distributed.device_mesh import init_device_mesh
from torch.distributed.tensor.parallel import (
ColwiseParallel,
RowwiseParallel,
parallelize_module,
)
from torch.distributed._composable.fsdp import fully_shard
class MLP(nn.Module):
def __init__(self, d):
super().__init__()
self.w1 = nn.Linear(d, 4 * d, bias=False)
self.w2 = nn.Linear(4 * d, d, bias=False)
def forward(self, x):
return self.w2(torch.nn.functional.gelu(self.w1(x)))
class Block(nn.Module):
def __init__(self, d):
super().__init__()
self.attn = nn.MultiheadAttention(d, 8, batch_first=True)
self.mlp = MLP(d)
self.n1 = nn.LayerNorm(d)
self.n2 = nn.LayerNorm(d)
def forward(self, x):
x = x + self.attn(self.n1(x), self.n1(x), self.n1(x), need_weights=False)[0]
x = x + self.mlp(self.n2(x))
return x
def build_model(num_layers=12, d=1024):
return nn.Sequential(*[Block(d) for _ in range(num_layers)])
def main():
dist.init_process_group("nccl")
world = dist.get_world_size()
rank = dist.get_rank()
torch.cuda.set_device(rank % torch.cuda.device_count())
# 2D mesh:行是 DP(FSDP),列是 TP
tp_size = 2
dp_size = world // tp_size
mesh = init_device_mesh(
"cuda",
mesh_shape=(dp_size, tp_size),
mesh_dim_names=("dp", "tp"),
)
model = build_model().cuda()
# === TP:把每个 Block 的 MLP 列切 + 行切 ===
tp_mesh = mesh["tp"]
for block in model:
parallelize_module(
block.mlp,
tp_mesh,
{
"w1": ColwiseParallel(),
"w2": RowwiseParallel(),
},
)
# === FSDP2:按 Block 粒度做 ZeRO-3 切分 ===
dp_mesh = mesh["dp"]
for block in model:
fully_shard(block, mesh=dp_mesh)
fully_shard(model, mesh=dp_mesh)
model = torch.compile(model)
opt = torch.optim.AdamW(model.parameters(), lr=1e-4)
x = torch.randn(4, 256, 1024, device="cuda")
for step in range(10):
loss = model(x).square().mean()
loss.backward()
opt.step()
opt.zero_grad()
if rank == 0:
print(f"step {step} loss {loss.item():.4f}")
if __name__ == "__main__":
main()启动:
torchrun --nproc_per_node=8 fsdp_tp_demo.pyPyTorch 2.4+ 提供
torch.distributed.pipelining(PP),基本接口:
from torch.distributed.pipelining import pipeline, SplitPoint, ScheduleGPipe, Schedule1F1B
stage_mod = pipeline(model, mb_args=(x,),
split_spec={"layer4": SplitPoint.BEGINNING})
schedule = Schedule1F1B(stage_mod, n_microbatches=8, loss_fn=loss_fn)
schedule.step(x, target=y)TorchTitan 里有 TP + PP + FSDP2 的完整 llama3 demo,可以直接参考。
作为对比,Megatron 的启动参数:
torchrun --nproc_per_node=8 --nnodes=128 \
pretrain_gpt.py \
--tensor-model-parallel-size 8 \
--pipeline-model-parallel-size 16 \
--num-layers 96 --hidden-size 12288 --num-attention-heads 96 \
--seq-length 2048 --max-position-embeddings 2048 \
--micro-batch-size 2 --global-batch-size 1024 \
--sequence-parallel \
--use-distributed-optimizer \
--num-layers-per-virtual-pipeline-stage 4 \
--recompute-activations --recompute-granularity selective \
--bf16 --use-flash-attn \
--transformer-impl transformer_engine \
--fp8-format hybrid --fp8-amax-history-len 1024 --fp8-amax-compute-algo max逐项意义:
--sequence-parallel:把 TP
不切分的张量(LayerNorm 输入等)按 seq 维切,激活 /=
TP。--use-distributed-optimizer:等价于
ZeRO-1,优化器状态按 DP 切。--num-layers-per-virtual-pipeline-stage 4:Interleaved
1F1B 的 v,进一步压气泡。--recompute-granularity selective:只
recompute 内存性价比高的 op。--fp8-format hybrid:E4M3 前向 + E5M2
反向(Transformer Engine 默认策略)。{
"train_micro_batch_size_per_gpu": 2,
"gradient_accumulation_steps": 16,
"zero_optimization": {
"stage": 3,
"overlap_comm": true,
"contiguous_gradients": true,
"reduce_bucket_size": 5e8,
"stage3_prefetch_bucket_size": 5e8,
"stage3_param_persistence_threshold": 1e6,
"offload_optimizer": {"device": "cpu", "pin_memory": true}
},
"bf16": {"enabled": true},
"activation_checkpointing": {
"partition_activations": true,
"contiguous_memory_optimization": true
}
}模型能塞进单卡(含优化器)?
├─ 是 → DDP / FSDP1 即可
└─ 否 → 看参数规模
├─ < 20B:FSDP2(或 ZeRO-3) + Activation Ckpt,单节点搞定
├─ 20B–200B dense:TP(node 内 ≤ 8) + PP(跨节点) + DP + SP
├─ > 200B dense:TP + PP + DP + SP + Interleaved 或 Zero Bubble
└─ MoE:EP(node 内或 node-limited) + PP + DP (+ 小 TP 或 无 TP) + DualPipe
并结合序列长度:
seq ≤ 8K : 不需要 SP
seq 8K–32K : Megatron-SP
seq ≥ 64K : Context Parallel / Ring Attention
seq ≥ 256K : CP + Activation Offload + FlashAttention
DP × micro_batch × grad_accum。Pre-train 常见
2M–4M tokens / step。| 场景 | MFU 参考线 |
|---|---|
| A100/H100 dense pretrain | 45–55% |
| H800 dense(砍 NVLink) | 35–45% |
| H800 MoE DualPipe | 35–42% |
| 长上下文(CP > 1) | 30–40% |
| 国产 910B / MX | 25–40%(和算子库成熟度强相关) |
MFU 30% 以下,先查:TP 是否跨节点;PP 气泡是否过大;ZeRO-3 是否 AllGather 打爆 IB;FlashAttention 版本;FP8 是否真开。
M ≥ 4P
是最低线。experts_per_rank = 1 时
all-to-all 代价 > 计算;建议 2–4。fp8_autocast 默认窗口 1 常常够用。把上面所有维度放到一次真实训练上算一笔账。假设:
L=96,H=12288,heads=96,seq=2048。global_batch = 4M tokens,MFU ≥
45%。选 TP=8, PP=16, DP=8。
TP=8 贴节点 GPU 数,MLP/Attn 的 AllReduce
全在 NVLink。PP=16 跨 16 个节点(每节点只属于一个 PP
stage 的一片),stage 间 P2P 走 IB。DP=8 跨剩下 8 组节点做 AllReduce,用 HSDP
即可。单卡参数:175B / (TP × PP) = 175B / 128 ≈ 1.37B,FP16
权重 2.74GB,加梯度 /
优化器:2.74 + 2.74 + 16.4 ≈ 22GB。
激活(全存):L/PP × B × S × H × 12 bytes ≈ 6 × 2 × 2048 × 12288 × 12 ≈ 3.6GB
/ micro-batch(FlashAttention 下)。若 M=32
micro-batch,1F1B 峰值
P × 3.6GB ≈ 58GB——加权重已 80GB,必须开
selective recompute。
单步数据流:
4 × B × S × H × 2 = 4 × 2 × 2048 × 12288 × 2 ≈ 400MB / layer,NVLink
900GB/s 下 ≈ 0.45ms。96 层 × 4 次 ≈ 170ms(纯通信)。B × S × H × 2 ≈ 100MB,IB 400Gbps(有效
~40GB/s)下 ≈ 2.5ms / 次。2 × 1.37B × 2 bytes ≈ 5.5GB,IB 下 150ms
级,必须被反向 overlap。单 micro-batch 纯前向计算时间约
6 × N × B × S / (FLOPS × MFU) = 6 × 175e9 × 2 × 2048 / (989e12 × 0.5) ≈ 8.7s / step(全模型)。分到
PP 每 stage ≈ 540ms;B 与 F 比 ≈ 2:1,所以 1F1B 一个
micro-batch 单 stage 耗时约 1.6s。
气泡 (P-1)/M = 15/32 ≈ 47%,太高!方案:
生产上 Megatron 默认 Interleaved,DeepSeek V3 用 DualPipe。
1024 × H100 × 一年 ≈ $15–25M(含电 + 折旧),所以每 1% MFU 提升 = 每年省 15–25 万美金。这就是为什么所有大厂要自研调度器、死磕 FP8 和零气泡——算力涨 1% 都是真金白银。
pjit。Mesh + PartitionSpec
声明,编译期决定通信。jax.experimental.shard_map
的模型定义极简洁。--profile
flag:自动标注每段耗时。tokens/sec/GPU,算 MFU。低于目标 5%
以上就要查。async_op=False、或 bucket 太小。989 TFLOPS × 0.7 为单
GEMM 经验线)。amax_history,必要时换 E5M2 / 回 BF16。工业训练的大部分时间都在和一些”看不见的坑”做斗争。下面几例来自公开的故障复盘(OpenAI、Meta、DeepSeek、字节、百度等技术博客):
nvlink -s
自动巡检。NCCL_IB_TIMEOUT/NCCL_IB_RETRY_CNT
调大 + 网络侧 BER 监控。torch.save 单机写 1TB
权重写到一半掉线。方案:分片并行写 + 原子 rename +
异步(见第 10 篇)。这些坑没有哪一条能靠”看论文”避开,全是血泪。一套成熟训练系统的差异就体现在这里。
Q1:既然 ZeRO-3 能省到 1/N 显存,为什么还要 TP/PP? ZeRO-3 的代价是前 / 反向前要 AllGather 完整权重,跨节点带宽一被打爆,MFU 就崩。TP/PP 切的是”永久切”,权重常驻本地,通信量反而小。超 50B 后纯 ZeRO-3 不现实。
Q2:FSDP2 和 Megatron-LM 二选一怎么选? Python 代码可读性、快速试错、与 HF 生态打通 → FSDP2 / TorchTitan。 追求极限 MFU、成熟 3D 并行、FP8、稳定支持 671B 级 → Megatron-Core。
Q3:MoE 非得用 EP 吗? 小 MoE(< 8 expert)可以用 TP 切。但 expert 多起来(DeepSeek 256 个),TP 切不动,必须 EP。EP + all-to-all 的瓶颈正是当下的主战场。
Q4:PP 的 micro-batch 越多越好?
一定范围内是的。M ≥ 4P 气泡才可接受。但 M
太大会让激活存不下(1F1B 下激活 = P 份,不受 M 影响;GPipe
下 = M 份,会爆)。
Q5:ZeRO-Offload 到 CPU 真的能训 70B 吗? 能,但吞吐常是纯 GPU 方案的 1/5–1/3。更适合 SFT / LoRA / 消融实验。
Q6:TP 为什么不能跨节点? TP 每层 AllReduce 几百 MB,一步几十次。NVLink 900GB/s 和 IB 50GB/s 差一个数量级,跨节点 TP 的通信时间会盖住 GEMM。
Q7:DualPipe 是银弹吗? 不是。显存要多一份权重副本,Dense 大模型不划算;对 MoE 的 all-to-all 重叠收益大。对上下文极长(激活巨大)场景也未必适合。
Q8:Sequence Parallel 和 Context Parallel 有什么差别? Megatron 术语里,SP 特指与 TP 合体的版本(切非线性算子的 seq 维,通信量不变)。CP(Context Parallel)切真正的 attention 部分 seq 维,跟 Ring Attention 同类。两者通常合起来用。
Q9:FlashAttention 与并行怎么搭? FlashAttention 本身不引入额外通信,与 TP/PP/DP 正交;与 CP 搭配时用 Flash-Attn 的 ring 变体(FA3 已内置)。
Q10:万卡训练什么时候必须上? 当 training tokens > 10T、model > 300B、或你想 3–4 周内出 checkpoint。小于这个规模,千卡 + 更好的工程更划算。
| 维度 | 通信 op | 通信量级 | 合适的域 |
|---|---|---|---|
| DP / DDP | AllReduce | O(N) | 任意(跨节点) |
| ZeRO-1 | AllReduce grad | O(N) | 任意 |
| ZeRO-2 | ReduceScatter + … | O(N) | 任意 |
| ZeRO-3 / FSDP | AllGather + RS | 1.5×O(N) | 节点内优先,跨节点用 HSDP |
| TP(Megatron) | AllReduce | O(a) × 层 | 节点内 |
| TP + SP | RS + AG | 同上 | 节点内 |
| PP | P2P send/recv | O(a) / stage | 跨节点 |
| Context Parallel | Ring send/recv | O(a) × ring | 节点内或专属 ring |
| EP | All-to-All × 2 | O(a × topk) | node-limited |
通用口诀:
并行不是把模型切开就完事,真正的工程在于:谁切、切到哪个通信域、与什么计算 overlap、在哪个阶段同步。
下一篇我们拆解最具代表性的两套实现:Megatron-LM 与 DeepSpeed。
上一篇:训练全景:Pre-train、SFT、RLHF、DPO、蒸馏 下一篇:Megatron-LM 与 DeepSpeed
把当前热点继续串成多页阅读,而不是停在单篇消费。
2026-04-22 · architecture / ai-infra
开源训练框架双雄对比,覆盖 Megatron-LM、DeepSpeed、FSDP2、torchtitan、Colossal-AI,含选型与工程实操。
2026-04-22 · architecture / ai-infra
面向工程师的大模型基础设施开篇地图,覆盖 2022 到 2026 的工程分水岭、五层工程栈、训练与推理的工程差异、中国与全球行业版图以及成本曲线。
2026-04-22 · architecture / ai-infra
从 CPU 与 GPU 的架构差异出发,讲清楚 SM、Warp、Tensor Core、HBM、NVLink 的工程含义,并结合 Roofline、FlashAttention 与国产算力栈,给出大模型工程师能直接上手的 GPU 心智模型。
2026-04-22 · architecture / ai-infra
从 nvcc 到 Triton,把 NVIDIA 软件栈的每一层拆给大模型工程师看,顺便谈谈 ROCm、CANN 为什么一直追不上。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。