Chapter 05

3D 并行的实现:TP / PP / DP 各占几行

📌 commit 2411b02读源码学分布式

nanotron 的最大教育价值是"3D 并行实现都很短"。本章把 TP / PP / DP 的核心代码摘出来看。

5.1TP:TensorParallelColumnLinear(~100 行)

src/nanotron/parallel/tensor_parallel/nn.py

class TensorParallelColumnLinear(nn.Linear):
    def __init__(self, in_features, out_features, pg, mode, ...):
        # 切 output 维度
        local_out = out_features // pg.size()
        super().__init__(in_features, local_out, bias=bias)
        self.pg = pg                       # tp process group
        self.mode = mode                    # ALL_REDUCE / REDUCE_SCATTER

    def forward(self, x):
        # 1) input:所有 rank 完整
        if self.mode == "ALL_REDUCE":
            y = F.linear(x, self.weight, self.bias)
            return y                       # output 是 sharded(每 rank 1/N 列)
        else:  # REDUCE_SCATTER
            x_full = all_gather(x, self.pg)
            y = F.linear(x_full, self.weight, self.bias)
            return y
class TensorParallelRowLinear(nn.Linear):
    def __init__(self, in_features, out_features, pg, mode, ...):
        local_in = in_features // pg.size()
        super().__init__(local_in, out_features, bias=bias)
        ...

    def forward(self, x):
        # x 是 input 维切的
        y = F.linear(x, self.weight, self.bias)
        if self.mode == "ALL_REDUCE":
            y = all_reduce(y, self.pg)     # 完整 output
        else:
            y = reduce_scatter(y, self.pg)
        return y

就这么简单。Llama 的 Attention 用 ColumnLinear (q/k/v) + RowLinear (o),MLP 类似。

5.2PP:1F1B schedule(~200 行)

src/nanotron/parallel/pipeline_parallel/engine.py

class OneForwardOneBackwardPipelineEngine:
    def step(self, model, data_iter, criterion, optimizer):
        num_microbatches = config.num_microbatches
        pp_size = pg.size()

        # warm-up:每个 stage 跑 (pp_size - pp_rank - 1) 个 forward
        warmup_steps = pp_size - pp_rank - 1
        for _ in range(warmup_steps):
            input = recv_from_prev_stage()
            output = forward(model, input)
            send_to_next_stage(output)

        # 1F1B:每跑一个 forward 跑一个 backward
        for _ in range(num_microbatches - warmup_steps):
            input = recv_from_prev_stage()
            output = forward(model, input)
            send_to_next_stage(output)
            grad = recv_grad_from_next_stage()
            input_grad = backward(output, grad)
            send_grad_to_prev_stage(input_grad)

        # cool-down:剩下的 backward
        for _ in range(warmup_steps):
            grad = recv_grad_from_next_stage()
            input_grad = backward(output, grad)
            send_grad_to_prev_stage(input_grad)

实际代码有更多细节(buffer 管理 / loss accumulation),但核心就这个 schedule。

5.3DP:DistributedDataParallel + 梯度 all-reduce

nanotron 没用 PyTorch 自带的 DDP,而是自己实现了一个:

# src/nanotron/parallel/data_parallel/utils.py
def sync_gradients(model, dp_pg):
    """backward 完后调一次,把每张卡的梯度 all-reduce 求平均"""
    for p in model.parameters():
        if p.grad is not None:
            dist.all_reduce(p.grad, op=dist.ReduceOp.AVG, group=dp_pg)

核心就一个 all-reduce 循环。生产代码还有梯度 bucket、async 通信等优化。

5.4三种并行怎么组合

src/nanotron/trainer.py 训练循环:

def training_step(self, batch):
    if self.pp_size > 1:
        loss = self.pp_engine.step(self.model, batch, self.criterion, self.optimizer)
    else:
        with self.tp_context():
            output = self.model(batch)
            loss = self.criterion(output, batch.target)
            loss.backward()

    # DP 同步梯度
    if self.dp_size > 1:
        self.sync_gradients()

    self.optimizer.step()
    self.optimizer.zero_grad()

清晰的三层:TP 内部 forward;PP schedule 控制 stage 之间;DP 同步梯度。

5.5TP mode:ALL_REDUCE vs REDUCE_SCATTER

ALL_REDUCEREDUCE_SCATTER(SP 风格)
TP 之间通信output 在 RowLinear 处 all-reduceoutput 在 RowLinear 处 reduce-scatter(结果 seq 切)
activation 内存full sequence × full hiddenseq/N × full hidden
需配合 SequenceParallel需要
通信量所有 token 的 hidden1/N token

大模型 / 长序列推荐 REDUCE_SCATTER 模式(即 Megatron 的 SP)。

5.6这章你需要带走的