最近在折腾一个图像分类项目,单卡跑没问题,但想试试多卡加速。参考官方文档写了DistributedDataParallel,结果一跑就报“RuntimeError: NCCL error: invalid usage”。排查了半天,发现好像是我设了torch.cuda.set_device(local_rank)后,主进程和子进程的设备号对不上?我用的启动命令是torchrun --nproc_per_node=2 train.py,代码里也加了init_process_group(backend='nccl')。是不是我多卡初始化顺序有问题,或者环境变量没设全?有没有大佬能帮忙看看常见坑在哪?先谢谢了!
用PyTorch做多卡训练,DDP一直报错找不到设备,求指点
全部回复
共 147 条遇到过类似的坑,十有八九是环境变量没对齐。torchrun其实会自动设置RANK和LOCAL_RANK,你代码里如果手动set_device(local_rank)但没在init_process_group之前读一下os.environ,就容易出现主进程和子进程设备错乱。我一般习惯在init_process_group之后再set_device,或者干脆用torch.cuda.device(local_rank)上下文管理器包住训练循环。另外检查下NCCL_P2P_DISABLE这些变量,有时候驱动和卡间通信不兼容也会报invalid usage,可以先设成1试试能不能跑通。
试试把set_device放在init_process_group前面,很多坑都是顺序问题。
你这报错八成不是环境变量的问题,torchrun其实会自动设好RANK和LOCAL_RANK,你代码里只要用local_rank来set_device,然后DDP的device_ids也传local_rank就行。我之前踩过坑是忘了在init_process_group之后加torch.cuda.synchronize,导致NCCL初始化时设备还没就绪,你可以试试在初始化后面加一行。另外确认下PyTorch版本是不是1.9以上,老版本对torchrun支持有bug,换成python -m torch.distributed.launch也可能绕过去。
大概率是local_rank没从env里取,直接用了默认值,试试int(os.environ['LOCAL_RANK'])。
我之前也踩过这个坑,NCCL报invalid usage多半不是设备号对不上,而是初始化顺序或者环境变量的问题。你用了torchrun的话,其实不需要手动调set_device,torchrun会自动帮你把local_rank映射到对应的GPU上,手动设置反而容易跟它内部逻辑冲突。我之前是把set_device那行删掉,然后直接用torch.cuda.current_device()获取当前进程的GPU,就正常了。另外你检查下有没有设MASTER_ADDR和MASTER_PORT,虽然torchrun会默认填,但如果代码里别的地方覆盖了就麻烦。还有一个常见问题是init_process_group的world_size要跟nproc_per_node保持一致,但有时候你如果用了launcher,它自己会传环境变量,你代码里再硬编码一个值就可能出错。建议你先把set_device改成从环境变量里读LOCAL_RANK,然后print出来看下两个进程的rank和device对不对,NCCL的报错有时候很笼统,实际是后面的同步操作出了问题。我之前还遇到过因为数据集每个卡都要重新加载,导致某个进程卡住,最后触发NCCL超时,报错也长这样,你可以把dataloader的num_workers调小试试。
这问题我上周刚踩过,大概率是init_process_group里没传rank和world_size,torchrun虽然会设置环境变量,但代码里得用dist.get_rank()取,不能自己写死local_rank。另外set_device要放在init_process_group之后,顺序反了NCCL就会找不到卡。你可以把RANK和LOCAL_RANK打印出来对比下,主进程和子进程应该分别是0/0和1/1,对不上就是环境变量读取的问题。
这问题我踩过坑,大概率不是环境变量没设全,而是你代码里对device的赋值方式跟torchrun的默认行为打架了。torchrun会自动帮你设置LOCAL_RANK和CUDA_VISIBLE_DEVICES,但如果你在代码里又手动调torch.cuda.set_device(local_rank),同时后续创建模型时还用了.cuda()而不是显式传device参数,就容易出现主进程和子进程看到的是不同物理卡的情况。我之前就是没注意,明明设置了local_rank,但模型参数还是跑到了默认卡0上,然后NCCL一通信就直接invalid usage。
建议你检查一下,init_process_group之前是不是先调用了set_device,顺序搞反了也会出问题。另外,如果你用的是torchrun,其实可以完全不写set_device,直接让torchrun帮你管理,然后所有张量都通过device = f'cuda:{local_rank}'来创建。还有个隐蔽坑,就是数据加载器里如果用了num_workers>0,每个worker也会继承环境变量,但它们的rank可能没被正确重设,导致NCCL通信时某个进程找不到对应设备。
你试试把代码改成:先init_process_group,再获取rank和local_rank,然后设置device,最后再构建模型和DDP包装。如果还报错,可以加个环境变量NCCL_DEBUG=INFO跑一下,看具体是哪个rank在哪个阶段找不到设备。我之前就是这么定位到是数据加载器里一个自定义collate函数隐式调用了.cuda()导致的,真是巨坑。
遇到过类似的坑,大概率是local_rank和set_device的配合问题。torchrun会自动设置LOCAL_RANK环境变量,但如果你在代码里手动用torch.cuda.set_device(local_rank),得确保是在init_process_group之后调用,而且最好直接用rank % torch.cuda.device_count()来算设备号,别直接拿local_rank当索引。另外检查一下是不是忘了设MASTER_ADDR和MASTER_PORT,虽然torchrun会默认给,但如果你在代码里覆盖了就可能出问题。我之前是把init_process_group放在set_device前面就解决了,你可以试试调整顺序。
大概率是init_process_group里忘传rank和world_size了,torchrun虽然会设环境变量但代码得自己读。试试在初始化前打印下环境变量对下号。
我之前也踩过这个坑,NCCL那个报错十有八九就是设备号没对齐。你用了torchrun的话,其实不需要自己调torch.cuda.set_device,它内部已经帮你把环境变量和当前进程的GPU绑定了,你手动再设一遍反而容易跟local_rank的映射冲突,尤其是多机多卡的时候特别容易出这种诡异问题。我当时的解决方法是把set_device那行删掉,直接靠torch.cuda.device(local_rank)上下文管理器来包裹模型和数据,或者干脆在init_process_group之前先打印一下rank和cuda:0的可见性,确认每个子进程看到的GPU是不是各自独立的那张。另外你检查下NCCL_P2P_DISABLE和NCCL_SHM_DISABLE这两个环境变量,有些老驱动或者容器环境没开共享内存,也会冒出来invalid usage,我之前在docker里跑就靠这个绕过去的。还有个小细节,init_process_group里最好显式指定world_size和rank,别完全依赖torchrun的默认值,有时候版本不一致会漏传。你先试试把set_device去掉,然后把环境变量加上,大概率就好了。如果还不行,建议把完整的traceback贴出来,光看这一句很难定位是卡在broadcast还是allreduce。
我之前也踩过这个坑,NCCL报invalid usage大概率不是环境变量的问题,而是rank和device绑定的时机不对。你用了torchrun,其实它会自动帮你设置好RANK、LOCAL_RANK这些环境变量,所以完全不需要手动调torch.cuda.set_device,直接在init_process_group之后用torch.device('cuda', local_rank)创建模型就行。我之前就是先设了set_device再初始化进程组,结果主进程和子进程的cuda context就乱了,跟你描述的现象一模一样。还有个容易忽略的点是,数据加载器里的worker数不能设成0,否则多卡通信时会卡住或者报奇怪的NCCL错误。另外你检查一下是不是所有进程都执行到了同一个barrier,比如有的进程提前退出了,剩下的就会等超时然后报这个错。我建议你把init_process_group里的init_method显式写成env://,并且确认nodes和node_rank参数在torchrun下不用自己传。最后实在不行可以先降级到gloo后端跑通流程,确认逻辑没问题再切回nccl,这样排查起来快很多。
这问题我踩过,多半是local_rank和cuda device没对齐。torchrun会自动设置环境变量,但你在代码里如果再手动set_device,容易把进程和卡号搞乱,尤其是子进程的rank和实际GPU id对不上。建议直接用os.environ['LOCAL_RANK']来设device,别自己传参。另外确认下init_process_group里有没有写world_size,有时候漏了它也会触发NCCL初始化异常。我之前加了个torch.cuda.empty_cache()在初始化前,莫名就稳了,你可以试试。
遇到过类似的坑,NCCL报invalid usage大概率就是rank和device没对上。你用torchrun的话,其实不用手动调set_device,它会自动根据local_rank分配,你可以在init_process_group之后打印一下rank和device确认一下。另外检查下环境变量里有没有设CUDA_VISIBLE_DEVICES,有时候这个会影响NCCL的通信。我之前是把init_process_group放在set_device前面就好了,你可以试试调换下顺序。
这报错八成是device id和进程绑定的问题,torchrun会自动帮你设置好环境变量,但如果你在init之前就手动set_device,容易把主进程的卡号弄乱。我习惯是在init_process_group之后再用torch.cuda.set_device(local_rank),或者干脆不写,靠distributed模块自己去对应。还有个小细节,确保两个进程看到的是不同的GPU,你可以用nvidia-smi看看是不是都在抢0号卡,那样NCCL也会炸。
我之前用DDP也遇到过,后来发现是忘了在代码开头加os.environ['MASTER_ADDR']和MASTER_PORT,虽然torchrun会传,但自己设一下更稳。你试试把set_device那行删掉,直接在模型forward之前用torch.cuda.device(local_rank)上下文管理,这样每个进程自然就绑定到自己的卡了。要是还不行,把N
大概率是init_process_group里忘传了rank和world_size,torchrun不会自动补全这些参数。
八成是rank和device没对齐,试试在init_process_group后再set_device,或者直接用local_rank传参。
我之前也踩过这个坑,问题基本就出在init_process_group的调用时机上,set_device一定要放在它后面,而且local_rank得从环境变量里取,不能自己硬编码。你试试把torch.cuda.set_device挪到init_process_group之后,再用os.environ['LOCAL_RANK']拿当前进程的卡号,应该能解决。另外nccl报invalid usage有时候也是因为没设MASTER_ADDR和MASTER_PORT,虽然torchrun会默认给,但显式写上更保险。
我之前也踩过这个坑,大概率是torch.cuda.set_device和init_process_group的顺序反了。DDP要求先初始化进程组,再设置当前设备,不然NCCL会拿到错误的rank映射。你试试把init_process_group提到最前面,set_device紧跟其后,应该能解决。另外torchrun其实会自动帮你设好LOCAL_RANK环境变量,不用手动从args里读,检查下你是不是重复赋值了。
这问题我踩过类似的坑,大概率不是环境变量的问题,而是set_device和init_process_group的相对顺序没搞对。你试试把torch.cuda.set_device(local_rank)放到init_process_group之后,然后确保每个进程里都先拿到正确的local_rank再设置设备,别直接用全局rank。另外检查下train.py里有没有在创建模型之前就调用了任何cuda操作,有时候单卡没事但多卡就会因为设备上下文没切干净报NCCL错。我之前就是被一个sanity_check里的tensor.cuda()坑了,挪到DDP初始化后面就好了。
我之前也踩过这个坑,torch.cuda.set_device这步其实可以省了,torchrun会自己根据local_rank设好当前进程的GPU。你试试把set_device去掉,然后确保init_process_group在model.cuda()之前调用,顺序错了容易出这种诡异问题。另外nccl对显存和网络要求比较高,可以加两句环境变量:NCCL_DEBUG=INFO跑一下,看具体卡在哪个通信环节,比瞎猜快得多。
我之前也踩过这个坑,多半不是init的问题,而是torch.cuda.set_device和torchrun自动分配的LOCAL_RANK冲突了。你用torchrun启动的话,其实每个进程的local_rank环境变量已经帮你把当前进程的GPU定好了,没必要再手动设一遍device,直接把模型和数据.cuda(local_rank)就行。另外检查下是不是忘了在init_process_group里传rank和world_size,虽然torchrun会从环境变量读,但显式写出来更稳。我之前就是这两处改完就好了,你可以试试先去掉set_device看报错还在不在。
不过还有一种情况,如果显卡之间是不同型号或者驱动版本不一致,NCCL也会报invalid usage,这个就不好查了。你可以先用nccl换成gloo跑一次,如果通了说明就是NCCL通信的问题,再慢慢排查驱动和网络。总之先确认每个进程看到的GPU编号是0还是各自不同的值,打印一下最直观。