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 | 含义 |
|---|---|---|---|
RANK | 0 | 1 | 全局 rank,跨所有节点唯一 |
LOCAL_RANK | 0 | 1 | 节点内编号,直接对应 GPU 序号 |
WORLD_SIZE | 2 | 2 | 全局进程总数 |
LOCAL_WORLD_SIZE | 2 | 2 | 本节点进程数 |
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 为例:
- 父进程启动:解析参数;单机模式自动生成
MASTER_ADDR=127.0.0.1并分配空闲端口 - 生成子进程:拉起 2 份
train.py,按上表注入环境变量 - 子进程各自初始化:代码走到
dist.init_process_group(backend="nccl"),读取环境变量建立进程组与通信通道 - 运行与回收:训练逻辑执行;父进程收集退出码,负责报错定位与弹性重启
单机用法
--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.pyrdzv 会合模式
多机场景更推荐 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 的三点关键升级:
- 环境变量传参取代
--local_rank命令行参数(旧方式需--use_env开启,脚本还得解析 argparse) - 弹性与容错:进程挂掉自动重启、支持节点动态增减;旧 launch 一个进程挂全部崩溃
- 内置 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 一行命令包装同等启动逻辑。整体并行策略选型见 分布式训练总览。