最近在折腾一个多节点分布式训练的任务,用的 PyTorch DDP,后端是 NCCL。环境是公司自研的 MCP 集群,网络走的是 InfiniBand。单机 8 卡跑没问题,但一扩展到两机 16 卡就频繁报“NCCL 超时”和“梯度同步失败”。我检查了网卡配置和 NCCL 版本,也试过调小 batch size 和梯度累积步数,还是偶尔会 hang 住。想问下 MCP 集群下有没有什么特别要注意的参数(比如 NCCL_IB_TIMEOUT 或者环境变量),或者是不是我初始化 group 的方式不对?真心被这个问题卡了三天了,求过来人指条明路。
MCP 环境下用 PyTorch 做分布式训练,梯度同步总是报错,有大佬指点吗?
全部回复
共 114 条遇到过类似的坑,试试NCCL_IB_TIMEOUT调大点,再检查下MCP的共享内存和网卡绑核,八成是拓扑感知没开。
之前调了两天,最后发现是MCP的虚拟化网卡和多进程绑定冲突,把NCCL_SOCKET_IFNAME指到具体IB口就稳了。
碰到这种多机扩展的问题确实头疼,我之前在自建集群上也被NCCL超时折磨过两周。你单机8卡正常但跨机就挂,大概率不是batch size的问题,而是IB通信的拓扑感知没做好。建议先确认一下MCP集群里每台机器的网卡是否都绑定了正确的IB设备,用ibstatus看看速率是否一致,有时候混合了不同速率的链路会导致NCCL初始化时选错路径。另外你提到的NCCL_IB_TIMEOUT,我一般会设成22或更高,默认的10秒在复杂网络里太容易触发了,还有NCCL_IB_RETRY_CNT也别忽略。不过我觉得最可疑的还是group的初始化方式,多机DDP一定要确保所有进程的rank和world_size完全一致,特别是用torch.distributed.init_process_group时,如果用的是env://,检查一下每台机器的MASTER_ADDR和MASTER_PORT是不是都能互通,很多hang住其实是某个worker压根没连上。你试过把NCCL_DEBUG=INFO打开看具体卡在哪个阶段吗?
先查下IB的GID索引和RDMA_CM是否一致,MCP上经常是网卡多租户隔离导致握手超时。
之前调K8s+DDP也踩过类似的坑,NCCL超时大概率不是batch size的问题,先查一下IB的active/disabled状态,ibstat看下端口速率是否一致。另外试试export NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,这俩对长消息传输的稳定性影响很大。还有你们MCP如果用了虚拟化或者容器,记得把NCCL_SOCKET_IFNAME指到具体的ib接口上,不然它可能走错网卡。group初始化本身一般没问题,但可以确认下每个进程的rank和world_size是不是从MCP的环境变量里读的,别硬编码。
两机16卡这种规模,NCCL超时大概率不是batch size的问题,先试试把NCCL_IB_TIMEOUT调到60以上,还有NCCL_IB_RETRY_CNT设成7或者更高,InfiniBand的丢包重试机制很关键。另外确认一下你的init_method是不是用的tcp://加头节点IP,如果是的话,换成env://或者file://有时候能避开一些诡异的握手问题。再检查下两机的网卡速率和MTU是否一致,MCP集群有时候会默认配错,导致跨节点通信卡在握手阶段。如果还不行,开下NCCL_DEBUG=INFO看下具体卡在哪个集合通信操作上,这样定位会比盲试快很多。
遇到过类似的坑,不过我们是在普通以太网环境下,但NCCL超时这个毛病太折磨人了。你单机8卡没事,两机就挂,大概率不是模型或代码逻辑问题,还是卡在跨节点通信上。建议先别急着调batch size,那玩意儿跟梯度同步超时关系真不大,核心还是看NCCL能不能在预期时间内完成allreduce。
我这边有个经验,就是MCP集群如果有自己的调度器,经常会限制共享内存或者锁页内存的大小,这会导致NCCL初始化时分配的缓冲区不够,但报错又很隐晦。你可以试试设置NCCL_P2P_LEVEL=0或者NCCL_SHM_DISABLE=1强制走IB,先排除共享内存路径的问题。另外NCCL_IB_TIMEOUT确实值得调,默认值在跨机高延迟下太保守了,我一般直接拉到22或者更高,配合NCCL_IB_RETRY_CNT=7,能缓解不少偶发超时。
还有个容易忽略的点,就是初始化group时用的网络接口名。PyTorch DDP默认会自己选网卡,但MCP集群里可能存在多个虚拟接口,它选错了就可能走不了IB。你手动指定一下NCCL_SOCKET_IFNAME和NCCL_IB_HCA,确保用的是同一个物理设备。我上次就是没指定HCA,结果两个节点各选各的端口,导致握手一直失败hang住。
另外你提到“梯度同步失败”是在训练中途还是刚开始就出现?如果是跑了一段时间才崩,建议看一眼是不是有节点温度过高导致降频,或者IB交换机端口有丢包。可以用ibstat查一下端口状态,如果看到有重传错误,那纯靠调环境变量是治标不治本。实在不行,临时用GLOO后端跑通一次做对比,能帮你快速定位是NCCL的问题还是集群本身的问题。
我之前也踩过类似的坑,NCCL_IB_TIMEOUT确实要重点看下,InfiniBand下默认值容易偏小,我们当时调到30以上才稳。另外你试试设NCCL_IB_RETRY_CNT=7,配合NCCL_IB_GID_INDEX=3,很多IB环境靠这个救回来。还有个小细节,MCP集群如果启用了共享内存或者特殊的调度策略,init_group时最好显式指定device_id,别靠torch自动探测,容易绑错网卡。最后实在不行可以用GLOO后端先排除网络问题,虽然慢但能定位是不是NCCL本身和IB驱动不匹配。
这问题我太有共鸣了,之前我们这边也踩过一模一样的坑,单机好好的,一上多机就NCCL超时。你先别急着调batch size,大概率不是显存或算力的问题,而是IB通信链路没完全打通。MCP环境下最容易被忽略的是NCCL的拓扑感知,它默认会去探测网卡和GPU的亲和性,如果驱动或者固件版本不匹配,很容易选错通信路径。建议你先跑一下nccl-tests里的all_reduce benchmark,看两机间的实际带宽和延迟是不是正常,如果数据很难看,那基本就是IB配置问题。另外,NCCL_IB_TIMEOUT确实要调,但更关键的是NCCL_IB_RETRY_CNT和NCCL_SOCKET_IFNAME,有些集群里需要显式指定走IB的网卡,否则它会偷偷走以太网兜底。还有,检查一下你的init_method,如果是用的tcp://方式,确保那台master节点的端口在所有机器上都放通了,有时候防火墙把端口半关了,就会表现为偶发hang。最后,试试把NCCL_DEBUG=INFO打开跑一次,看它卡在哪个具体的collective上,那个信息比啥都有用。如果还是不行,把PyTorch降到2.1以下或者升到2.3以上,有些版本对IB动态路由的兼容性有bug。
之前调过类似的,MCP环境下NCCL对IB的timeout特别敏感,建议先把NCCL_IB_TIMEOUT调到30以上试试,另外NCCL_SOCKET_IFNAME必须明确指到IB网卡,不然容易走上默认的TCP路径导致同步卡死。还有你初始化group用的是init_process_group吗?确认一下多机的world_size和rank传对了,特别是有时候host文件顺序不一致会引发隐性问题。梯度累积步数调小其实帮助不大,这个更像网络拓扑没被NCCL正确识别,可以试试NCCL_DEBUG=INFO跑一次,看它选用的transport是不是IB。如果还hang,检查一下IB的拥塞控制参数,有些MCP集群默认的qos设置会限速。
遇到过一模一样的坑,当时最后发现是防火墙把IB的7000端口给拦了,单机内网通但跨节点就超时。你先nccl-tests那种小包跨节点测下带宽,排除硬件层面的问题。另外NCCL_P2P_LEVEL别设成NVL,多机环境下需要靠IB做peer通信,设成PXB或者直接不设让它自动探测更稳。调batch size和梯度累积没用的,这问题八成出在nccl的通信拓扑上,试试把NCCL_MIN_NCHANNELS强制设成2或者4,有时候默认channel数太多在
试试把NCCL_IB_TIMEOUT调到60,再加NCCL_IB_RETRY_CNT=7,大概率能解决超时。
我们之前也遇到过,后来发现是MCP的网卡没开PFC流控,光调环境变量治标不治本。
试试把NCCL_IB_TIMEOUT调到60以上,还有NCCL_IB_RETRY_CNT设成7,大概率能稳。
遇到过类似的,检查下MCP的共享内存和网卡IP绑定的node-rank对不对,group初始化用init_method=“tcp://”加主节点IP试试。
遇到过类似的坑,MCP下两机16卡比单机8卡敏感太多了。你先试试把NCCL_IB_TIMEOUT调大到60甚至90,默认22秒在IB拥塞时真不够用,另外NCCL_IB_RETRY_CNT也顺手设个7。还有个小细节,init_method如果用tcp://,确保是rank0的IP而不是hostname,有的集群DNS解析会卡住导致假死。我之前还踩过网卡绑定的雷,多机时得确认每台机器都只用同一个IB设备,不然NCCL会自己乱选。要是还hang,建议开NCCL_DEBUG=INFO跑一次,看卡在哪个collective上,能省很多排查时间。
碰到这种问题确实很磨人,我上个月在类似的集群上也被折腾过。你单机没问题但跨节点就报错,大概率不是模型或DDP初始化的问题,而是NCCL在InfiniBand上的通信拓扑没被正确识别。先试试直接设NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7这两个变量,很多默认值在MCP这种虚拟化环境里都偏短,容易误判超时。另外,你检查过NCCL_SOCKET_IFNAME吗?MCP集群经常有多张网卡,如果NCCL选错了接口,哪怕IB配置对了也会hang。还有个坑是共享内存,/dev/shm太小会导致同步失败,可以加NCCL_SHM_DISABLE=1强制走IB试试,虽然会慢一点但至少能定位问题。至于group初始化,如果你用的是init_process_group,确认一下init_method是不是用了tcp://加正确的rank0地址,有些MCP环境对hostname解析有隔离,直接写IP更稳。最后建议你开NCCL_DEBUG=INFO跑一次,看它卡在哪个collective上,比盲调参数快得多。如果还不行,试试把torch.backends.cudnn.benchmark关掉,我之前发现它会影响某些算子的执行时间,间接导致同步窗口错位。
碰到过类似的情况,当时也是两机16卡NCCL timeout,最后发现是IB的GID配置没对齐。MCP集群如果用了多个子网,或者交换机做了分区,NCCL默认选的IB设备可能不是最优路径,你可以先用ibstatus和ibv_devinfo确认下两边的设备状态,然后手动设NCCL_IB_HCA绑到同一个物理端口试试。另外你提到初始化group的方式,我猜你是用init_method="env://"?如果两机时间戳没同步,或者rank的IP解析有问题,也会导致握手超时,可以试试改成tcp://加显式地址,或者直接用torchrun的--rdv_endpoint参数。还有个坑是NCCL的buffer大小和拓扑检测,MCP这种定制环境里,多机间可能因为PCIe switch的拓扑差异导致同步hang,你可以设NCCL_IB_TIMEOUT=22(这个值偏保守但稳定),同时加NCCL_IB_RETRY_CNT=8,再观察下。如果还不行,建议开NCCL_DEBUG=INFO,它会打印每次同步的耗时和具体卡在哪个rank,我当时就是这么定位到是第三块卡的lid路由问题。最后问下你调小batch size是全局调的,还是只改了每卡batch?DDP里这两个效果不一样,后者会影响梯度计算图的对齐。
NCCL_IB_TIMEOUT调大点试试,另外检查下两台机器的IB网卡速率和MTU是否一致,之前我们也是这问题。
试试把NCCL_IB_TIMEOUT调到30以上,另外确认下MCP的共享内存和网卡绑定,我们之前就是卡在这俩坑上。
我们之前也踩过类似的坑,MCP环境里NCCL对IB的拓扑感知特别敏感,建议先确认下两台机器的ibstat是不是连在同一台交换机上,跨交换机经常超时。另外可以试试显式设置NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,比默认值抗造很多。还有个小细节,DDP初始化group时最好用init_method="tcp://"指定主节点IP,别用env://,我们遇到过一次因为hostname解析导致的假死。如果还不行,把NCCL_DEBUG=INFO打开看下卡在哪个stage,大概率是allreduce之前的通信建立阶段。
碰到这种两机就挂单机没事的情况,我第一反应就是IB通信那块的问题,NCCL对网卡和拓扑的敏感度真的高。你试过单独设NCCL_IB_TIMEOUT=22或者更大吗?有时候默认值在跨机场景下根本不够用,尤其是MCP这种自研调度器可能会抢占带宽或者做虚拟化,导致实际报文延迟比预期高很多。另外,你确认过两台机器的IB网卡速率和MTU一致吗?不一致的话NCCL很容易在握手阶段就卡住,报超时反而是表象。
还有个思路,你试试用NCCL_DEBUG=INFO跑一次,看它卡在哪个collective上,是allreduce还是broadcast,这能直接定位是网络层还是group初始化的问题。我个人经验是,多机时init_process_group的backend='nccl'没问题,但init_method如果用tcp://加单点IP,MCP集群里如果节点IP不固定或者有防火墙策略,就容易hang。建议换成共享文件系统的方式,比如file://,让所有节点同时读同一个init文件,这样同步更稳。另外,你可以检查下MCP是不是默认禁用了GID索引或者RoCE v2,有时候需要显式设NCCL_IB_GID_INDEX=3才能通。
如果还是不行,试试把NCCL_SOCKET_IFNAME指定成具体的IB接口名,别让它自动选,有时候它会选到eth0上去导致跨机走TCP兜底,性能直接崩塌。最后提醒一句,梯度累积步数调小确实能减少同步频率,但没解决根因,建议先把NCCL_BUFFSIZE调大点,比如256M,能缓解小消息过多时的拥塞。三天不算啥,这种问题我调过一周,最后发现是MCP的cgroup限制了IB的memory lock,你可以查下ulimit -l,把锁内存上限放开试试。
我之前也踩过类似的坑,特别是跨节点时NCCL对IB和TCP的融合很敏感。你试试把NCCL_IB_TIMEOUT调到30以上,同时加上NCCL_IB_RETRY_CNT=32,另外确认一下NCCL_SOCKET_IFNAME是不是指向了正确的IB接口,有时候默认走eth0会莫名卡住。初始化group的话,如果你是直接用的torchrun,记得把MASTER_ADDR和MASTER_PORT显式传给每个节点,别依赖自动发现,我这边就是这俩没对齐导致hang。还有个偏方,把NCCL_DEBUG=INFO打开看下日志里到底卡在哪个集合操作上,通常能看出是allreduce还是broadcast的问题。
NCCL_IB_TIMEOUT调到30以上,再把NCCL_IB_RETRY_CNT设成16试试,之前我们也是这么解决的。