最近在折腾一个多节点分布式训练的任务,用的 PyTorch DDP,后端是 NCCL。环境是公司自研的 MCP 集群,网络走的是 InfiniBand。单机 8 卡跑没问题,但一扩展到两机 16 卡就频繁报“NCCL 超时”和“梯度同步失败”。我检查了网卡配置和 NCCL 版本,也试过调小 batch size 和梯度累积步数,还是偶尔会 hang 住。想问下 MCP 集群下有没有什么特别要注意的参数(比如 NCCL_IB_TIMEOUT 或者环境变量),或者是不是我初始化 group 的方式不对?真心被这个问题卡了三天了,求过来人指条明路。
MCP 环境下用 PyTorch 做分布式训练,梯度同步总是报错,有大佬指点吗?
全部回复
共 114 条遇到过,先试试NCCL_IB_TIMEOUT调大到60,再把NCCL_IB_RETRY_CNT设成7,大概率能缓解超时。
之前我们也是两机就hang,后来发现是MCP的RDMA网卡没开GPU Direct,查下NCCL_DEBUG=INFO看下走没走IB。
NCCL_IB_TIMEOUT 确实值得调,InfiniBand 环境下默认值经常不够,我一般是设成 60 甚至 120 再配合 NCCL_IB_RETRY_CNT 一起试。另外你检查过两台机器的 IB 链路协商速率没,之前我遇到过一台上是 200G 一台上是 100G,直接导致超时。还有个小细节,DDP 初始化时 world_size 和 rank 的映射顺序跟节点间通信也有关系,你可以打印下每张卡的 rank 和 device 索引确认下。
我之前也被MCP集群上的NCCL超时折磨过,最后发现是IB的GID索引没对上,尤其是多机的时候,默认的RoCE或者IB路径选择会跟预期不一致。你可以先试试设NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,这两个对偶发hang很管用,但别指望根治。更关键的是检查一下NCCL_SOCKET_IFNAME,InfiniBand环境下有时候它会把ib和eth混着用,强制指定到ib0或ib1能避免很多奇怪的路由问题。另外你初始化group的方式,如果用的是init_method="tcp://",记得确认MASTER_ADDR和MASTER_PORT在两台机器上完全一致,包括端口是否被防火墙挡了——我遇到过类似情况,单机没问题是因为回环不走防火墙。还有一个坑是MCP集群的共享存储可能天生有锁竞争,torch.load和save在每轮checkpoint时会卡住所有rank,导致NCCL超时,建议临时把checkpoint写到本地盘再异步同步。最后,试一下把NCCL_DEBUG=INFO打开,看它是卡在AllReduce的哪个阶段,如果是在ring上,多半是拓扑感知没生效,可以手动设置NCCL_TOPO_DUMP_FILE看它有没有正确识别IB拓扑。
遇到过类似的坑,MCP集群上NCCL对IB网络的自检很敏感,你先试试把NCCL_IB_TIMEOUT调大到60以上,同时加个NCCL_IB_RETRY_CNT=15,能解决大部分偶发超时。另外检查下NCCL_SOCKET_IFNAME是不是正确指向了IB接口,有时候默认选了docker0或者别的虚拟网卡导致握手失败。我们之前还发现init_process_group里如果没显式指定init_method,多机下会随机选节点当master,建议用tcp://固定到第一台机器的IP和端口,顺便把NCCL_DEBUG=INFO打开看下卡在哪个阶段。
遇到过类似的情况,不过我们当时是用的RoCE,后来排查下来问题出在MCP集群默认把IB的MTU设成了2048,而NCCL那边没感知到,导致跨节点通信时数据包分片严重,超时和hang就特别频繁。你可以先确认下两边的ibstat和ibv_devinfo输出是不是一致,尤其是active_mtu和端口速率。
另外NCCL_IB_TIMEOUT这个变量确实很关键,但光调大它治标不治本,我建议你把NCCL_DEBUG=INFO打开,看它卡在哪个集合通信原语上,是allreduce还是broadcast,这样能定位是拓扑感知问题还是网络路由问题。
初始化group的方式倒不是重点,DDP默认用env://就行,但你得确保每台机器的rank和world_size是通过环境变量正确传进去的,尤其是MCP这种自研调度器,有时候会屏蔽掉某些系统变量。还有个小坑,如果你们集群有防火墙或者安全组策略,偶尔会丢弃NCCL的UCX或IB建连包,导致hang住,可以试试把NCCL_SOCKET_IFNAME显式指定成ib0或者对应的IP段,绕过默认路由。
最后,实在不行就降级到gloo后端跑一版纯CPU的梯度同步,能通说明网络层没问题,问题就锁死在NCCL和IB驱动上。三天不算啥,我之前搞这个整整一周,最后发现是MCP的容器里缺了libibverbs的依赖,装上就好了。
我之前也踩过类似的坑,MCP环境下InfiniBand的拓扑感知特别重要,你试试设NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,这俩能扛住瞬时拥塞。另外检查一下两机之间的IB链路是不是走的同一个子网管理器,有时候跨子网会触发非预期超时。初始化group倒是次要的,DDP默认gloo+nccl混合就行,重点看NCCL_IB_DISABLE=0有没有被MCP的调度器覆盖。要是还hang,抓一下NCCL_DEBUG=INFO的日志,看看卡在哪个ring上,八成是某块网卡速率没协商到HDR。
我之前也遇到过类似的坑,多机扩展时NCCL超时大概率不是batch size的问题,而是IB网卡和NCCL版本之间的兼容性。你可以试试显式设置NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,这俩值对InfiniBand的稳定性影响很大。另外检查一下两机之间是否有防火墙或者路由配置挡了IB的通信端口,有时候group初始化没问题,但实际数据交换时网络栈就卡住了。如果还不行,建议把NCCL_DEBUG=INFO打开,看下日志里卡在哪个allreduce阶段,能直接定位是网络还是GPU侧的问题。
之前也踩过类似的坑,多节点下NCCL超时大概率不是batch size的问题,建议先看下IB的队列对数量和对端内存注册有没有限制。可以试试显式设NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,另外MCP集群如果启用了自适应路由,记得把NCCL_IB_DISABLE_OVERLAP设成1。初始化group的话,确认下init_method用的是tcp://主节点IP:端口而不是hostname,有时候解析会卡住导致hang。实在不行可以开NCCL_DEBUG=INFO看卡在哪个集联步骤,比盲调快多了。
我之前也踩过类似的坑,尤其是两机互联的时候NCCL超时大概率不是batch size的问题,而是IB通信的握手和超时配置没对上。你可以先试试把NCCL_IB_TIMEOUT调到30或者更大,默认的5在跨机高延迟下经常不够用,另外NCCL_IB_RETRY_CNT也建议设个7,这俩配合能解决大部分偶发hang的情况。还有个小细节,检查下两台机器的网卡速率和MTU是不是一致,MCP环境如果走的是虚拟化网络,MTU不一致会直接导致同步卡死。初始化group的方式一般不会引发这种问题,但你可以确认下init_method用的是tcp://还是file://,跨机时tcp需要确保端口没被防火墙挡,file方式则要挂载共享存储。如果还不行,试试把NCCL_DEBUG=INFO跑一次,看日志里是卡在allreduce还是ring setup阶段,那样能定位是拓扑感知问题还是驱动bug。另外你们MCP集群有没有限制共享内存大小?有时候shm不足也会伪装成NCCL超时,加个--shm-size=2g之类的参数试试。我最后是换了环境变量NCCL_SOCKET_IFNAME指定具体接口才彻底稳定的,你可以参考下。
我之前在别的集群上也踩过类似的坑,不过不是MCP,是自建的IB网络。你调NCCL_IB_TIMEOUT方向是对的,但我觉得更关键的是先确认一下NCCL是不是真的走了IB,有时候默认会回退到TCP socket,那超时和hang就非常随机了。可以用nvidia-smi topo -m看看节点间的P2P连接,再在代码里打印一下NCCL_DEBUG=INFO,看初始化时选的是不是ib。另外group初始化方式,如果用的是init_method="tcp://",注意那个ip要写主节点实际绑定的IB网卡IP,别写成管理网口,不然握手能通但数据传输会绕远路。还有个容易被忽略的是,两机16卡时,每台机器的rank分配和torchrun的nnodes参数要完全对上,尤其是环境变量MASTER_ADDR和MASTER_PORT在所有进程里要一致。你试过把NCCL_BUFFSIZE调大吗?我之前遇到类似hang,最后是把NCCL_SOCKET_IFNAME指定成ib0,再结合NCCL_IB_CUDA_SUPPORT=1才稳定下来。还有个小技巧,如果偶尔hang,可以在训练循环里加个watchdog,超时就重启该rank,虽然治标不治本,但能先跑通实验。你现在的NCCL版本是多少?有些老版本在IB上兼容性确实差。
遇到过类似的坑,不过我们当时是两机四卡,也是NCCL超时。你单机没问题但跨节点就挂,大概率不是代码逻辑,而是网络层面的东西,InfiniBand虽然快但配置要求很苛刻。我建议你先确认一下NCCL_SOCKET_IFNAME是不是指向了正确的IB接口,有时候默认走的是docker0或者别的虚拟网卡,这个变量没设对的话,即使IB驱动正常也会疯狂重试然后超时。另外NCCL_IB_TIMEOUT确实值得调,默认值在跨节点场景下经常偏小,我习惯直接设成22或者更高,单位是微秒,别被文档里那个看似很大的数字吓到。还有一个很隐蔽的点,就是MCP集群如果启用了自适应路由或者拥塞控制,NCCL可能会跟它冲突,可以试试设NCCL_IB_DISABLE=1强制走RoCE或者TCP先验证是不是IB的问题。group初始化方式的话,如果你用init_process_group时传的init_method是tcp://,那要确保那台rank0的机器端口能通,而且两台机器的网卡名要一致,不然NCCL在rank间同步时找不到对应设备。最后,batch size调小其实对hang住帮助不大,这个更像是通信缓冲或者拓扑感知的问题,你可以试试NCCL_TREE_THRESHOLD调整一下树算法和环算法的切换点,有时候小规模集群用环反而更稳。实在不行开NCCL_DEBUG=INFO看日志,它会打印每一步的握手和传输细节,定位到具体是哪个rank在等谁。
之前也踩过类似的坑,多节点下NCCL超时大概率不是batch size的问题,先看看NCCL_SOCKET_IFNAME是不是绑到了正确的IB接口,有时候默认走eth0会直接卡死。另外可以试试设NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,这两个组合我们调完基本就没再hang过。还有个小细节,init_process_group里保证所有节点的rank和world_size都从环境变量读,别硬编码,我们之前就是这里写错导致部分节点等待同步。如果还不行,开NCCL_DEBUG=INFO看下卡在哪个collective上,基本能定位到具体哪张卡出了问题。
之前我们这边也踩过类似的坑,多机DDP报NCCL超时八成不是batch size的锅,先看看NCCL_P2P_DISABLE和NCCL_SHM_DISABLE这两个变量在MCP下是不是被默认设成1了,InfiniBand的话得确保走IB而不是回落到TCP。另外你初始化group的时候,如果用了init_method="env://",检查下每台机器的rank和world_size映射对不对,我们当时就是rank顺序写反导致同步卡死。还有个偏方,把NCCL_IB_TIMEOUT调到60以上,同时加上NCCL_IB_RETRY_CNT=8,能缓解偶发hang,但治标不治本,最好还是看下你们MCP的虚拟化层是不是对IB的GID索引有限制。
NCCL超时这个坑我也踩过,MCP集群下IB网卡多节点时,光调NCCL_IB_TIMEOUT不够,建议先把NCCL_DEBUG=INFO打开,看卡在哪个rank的allreduce上。另外你检查过两台机器的IB的GID索引吗?我上次就是gid mismatch导致hang,改成一样的就好了。还有init_method用tcp://的话,试试换成共享文件系统的方式,有时候是元数据锁竞争。