torchrun 是 PyTorch 内置的分布式训练启动器,本质是一个进程管理器:拉起 N 个训练进程并注入分布式环境变量,为 torch.distributed 通信原语 的初始化铺路——它自己不做训练、不做张量通信。

本质:进程管理器

安装 PyTorch 即自带,无需额外安装;它只是 torch.distributed.run 模块的命令行入口,以下两条完全等价:

torchrun --nproc_per_node=2 train.py
python -m torch.distributed.run --nproc_per_node=2 train.py

职责边界一句话:批量拉起子进程 + 注入环境变量 + 监控其生命周期。真正的分布式初始化由训练脚本调用 dist.init_process_group() 完成,之后的梯度同步、集合通信全部由 NCCL/gloo 承担(见 AllReduce 集合通信)。若终端报 command not found,是虚拟环境未激活,找不到入口脚本。

环境变量注入机制

父进程 fork/spawn 出 N 份跑完全相同代码的子进程,仅靠环境变量区分身份。以 --nproc_per_node=2 为例:

环境变量进程 0进程 1含义
RANK01全局 rank,跨所有节点唯一
LOCAL_RANK01节点内编号,直接对应 GPU 序号
WORLD_SIZE22全局进程总数
LOCAL_WORLD_SIZE22本节点进程数
MASTER_ADDR / MASTER_PORT相同相同rank 0 进程的地址,各进程向它汇聚

脚本里 os.environ["LOCAL_RANK"] 能读到值,来源是 torchrun 父进程注入,而非 dist 库生成。init_process_group 默认 init_method="env://",即从这组变量读取拓扑完成进程组构建。

执行流程

以 torchrun --nproc_per_node=2 train.py 为例:

  1. 父进程启动:解析参数;单机模式自动生成 MASTER_ADDR=127.0.0.1 并分配空闲端口
  2. 生成子进程:拉起 2 份 train.py,按上表注入环境变量
  3. 子进程各自初始化:代码走到 dist.init_process_group(backend="nccl"),读取环境变量建立进程组与通信通道
  4. 运行与回收:训练逻辑执行;父进程收集退出码,负责报错定位与弹性重启

单机用法

--standalone 是最省事的形式:自动单节点、localhost、随机空闲端口,专供本机调试:

torchrun --standalone --nproc_per_node=4 train.py
参数说明
--nproc_per_node=N每节点进程数,通常等于 GPU 数;可写 gpu/auto 自动探测
--standalone单机模式,无需再设 master 与 node_rank

多机用法

静态模式

每个节点手动执行一条命令,用 node_rank 区分身份,全部指向 rank 0 节点的地址:

# 节点 0
torchrun --nnodes=2 --nproc_per_node=4 --node_rank=0 \
  --master_addr=192.168.1.10 --master_port=29500 train.py
# 节点 1:仅 node_rank 不同
torchrun --nnodes=2 --nproc_per_node=4 --node_rank=1 \
  --master_addr=192.168.1.10 --master_port=29500 train.py

rdzv 会合模式

多机场景更推荐 rendezvous(会合) 机制:所有节点执行完全相同的命令,通过 --rdzv_endpoint 指定的会合地址相互发现,动态分配全局 RANK,无需人工维护 node_rank:

# 每个节点执行同一条命令
torchrun \
  --nnodes=2:4 \
  --nproc_per_node=8 \
  --rdzv_backend=c10d \
  --rdzv_endpoint=192.168.1.10:29400 \
  --rdzv_id=job42 \
  train.py

不指定 rdzv 参数时走静态模式;c10d 是内置 TCPStore 会合后端,--rdzv_id 标识同一作业。

弹性训练

rdzv 模式的核心收益是容错:--nnodes=MIN:MAX 允许节点数在区间内动态伸缩——作业凑满 MIN 个节点即开跑,运行中扩容到 MAX 自动纳入。worker 崩溃后由 --max_restarts(默认 3)控制整组重启:父进程(elastic agent)杀掉全部存活 worker,重新会合、重放状态,而非直接放弃整个作业。配合 torch.distributed.elastic 的 checkpoint 钩子可实现断点续训(见 大模型检查点)。

相对旧 launch 的升级

torch.distributed.launch 已废弃移除,torchrun 的三点关键升级:

  1. 环境变量传参取代 --local_rank 命令行参数(旧方式需 --use_env 开启,脚本还得解析 argparse)
  2. 弹性与容错:进程挂掉自动重启、支持节点动态增减;旧 launch 一个进程挂全部崩溃
  3. 内置 rendezvous 会合服务,多机启动无需外部协调

易错点

  • torchrun 不会自动调用 init_process_group:脚本必须手写初始化,否则一切 dist API 报错。最小模板:
import os
import torch
import torch.distributed as dist
 
def main():
    dist.init_process_group(backend="nccl")  # 默认 env://,读 torchrun 注入的变量
    torch.cuda.set_device(int(os.environ["LOCAL_RANK"]))  # 绑定本进程专属 GPU
    model = torch.nn.Linear(4, 2).cuda()
    # DDP 包装、DistributedSampler、训练循环……
 
if __name__ == "__main__":
    main()
  • 一 GPU 一进程,是多进程而非多线程:各进程内存完全隔离,交换张量只能靠 dist 通信原语
  • 多机需保证节点间网络互通、端口防火墙放行;master 端口被占用会直接启动失败

不想手写这套样板代码,可用 Accelerate 一行命令包装同等启动逻辑。整体并行策略选型见 分布式训练总览。