最近在折腾一个多节点分布式训练的任务,用的 PyTorch DDP,后端是 NCCL。环境是公司自研的 MCP 集群,网络走的是 InfiniBand。单机 8 卡跑没问题,但一扩展到两机 16 卡就频繁报“NCCL 超时”和“梯度同步失败”。我检查了网卡配置和 NCCL 版本,也试过调小 batch size 和梯度累积步数,还是偶尔会 hang 住。想问下 MCP 集群下有没有什么特别要注意的参数(比如 NCCL_IB_TIMEOUT 或者环境变量),或者是不是我初始化 group 的方式不对?真心被这个问题卡了三天了,求过来人指条明路。
MCP 环境下用 PyTorch 做分布式训练,梯度同步总是报错,有大佬指点吗?
全部回复
共 114 条之前遇到过类似情况,两机16卡比单机8卡多出来的不只是卡数,跨节点通信的容错阈值完全不一样。建议先把NCCL_IB_TIMEOUT调大到60以上,同时加上NCCL_DEBUG=INFO看下具体卡在哪一步,有时候光调batch size没用,得看是不是网卡绑核或者IB的GID配置有问题。另外确认下MCP集群是不是要求用特定的NCCL socket类型,比如IB还是TCP,我们当时就是默认走了TCP导致超时。初始化group的话,记得把init_method换成tcp://主节点IP:端口,别用env://,在多机下容易出幺蛾子。
我之前在类似的MCP集群上也踩过这个坑,后来发现多半是IB网卡和NCCL的拓扑检测没对齐。你可以试试设NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,这俩对长尾超时很管用;另外确认一下NCCL_SOCKET_IFNAME指定的是ib接口而不是默认的eth,不然它可能走错网络。group初始化的话,建议用init_method="tcp://"加一个稳定的master地址,别用共享文件系统,MCP上锁经常出诡异问题。还有个小技巧,把NCCL_DEBUG=INFO打开看下到底是哪一步hang的,能省不少排查时间。
试试把NCCL_IB_TIMEOUT调到30以上,再加NCCL_IB_RETRY_CNT=7,我们之前卡死就是这么解决的。
试试把NCCL_IB_TIMEOUT调到30以上,再开NCCL_DEBUG=INFO看具体卡在哪个rank上,八成是IB网卡或交换机流控问题。
我们之前也踩过类似的坑,MCP集群的InfiniBand有时候会因为拥塞控制导致NCCL hang,你可以试试把NCCL_IB_TIMEOUT调到30以上,同时加上NCCL_IB_RETRY_CNT=8。另外检查下两机之间的网卡速率是否一致,我们当时是有一台机器降速了导致同步卡死。还有,如果你的PyTorch版本低于1.12,建议升级一下,老版本对多节点拓扑感知有bug。
同样被MCP坑过,InfiniBand下NCCL超时大概率不是算力问题,而是网络拓扑感知没开。试试设NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,另外检查下两台机器的IB网卡是否都在同一个子网,跨交换机容易丢包。还有初始化group时最好显式指定device_ids,别让它自动探测,我们当时就是漏了这个导致rank间通信错位。
我之前也踩过类似的坑,不过是在别的集群上。NCCL超时这东西,很多时候真不是batch size的锅,你先确认下两台机器的IB网卡是不是都在同一个子网里,然后试试手动跑一下nccl-tests里的all_reduce,看是不是单点带宽就有问题。另外,NCCL_IB_TIMEOUT这个变量确实值得调,默认值在有些IB交换机下太短了,我一般会设成22或者更长,但别超过30,不然hang住的时候半天不报错更难受。还有你初始化group的方式,如果用的是torch.distributed.init_process_group,记得确认init_method是tcp://还是env://,MCP这种自研集群经常有防火墙限制,tcp端口没开全就会偶发同步失败。我猜你八成是没设NCCL_IB_GID_INDEX,这个在IB驱动版本和子网配置不匹配时特别容易出问题,设成3或者1试试。最后,如果还卡着,可以把NCCL_DEBUG=INFO开起来,看看到底是卡在哪个ring的哪个节点上,有时候是某台机器的GPU间P2P没走IB,走了TCP fallback,那超时就是必然的。希望你能早点脱坑,这种问题排查起来太耗人。
我之前也遇到过类似的情况,单机正常一上多机就超时。你试过把NCCL_IB_TIMEOUT调大点没,比如设成60甚至120,InfiniBand的拥塞控制有时候挺吃这个的。另外确认下两机的网卡是不是都开了GDRDMA,有时候忘了设NCCL_IB_DISABLE=0会导致隐性问题。还有个小坑,group初始化用init_process_group时,保证所有节点的rank和world_size传对,别用ip:port那种老方式,容易卡在握手阶段。要是还hang,试试把NCCL_DEBUG=INFO跑一遍,看卡在哪个collective上,基本能定位是网络还是代码逻辑的问题。
我之前也踩过类似的坑,MCP集群上NCCL超时大概率不是batch size的问题,而是IB链路协商或者网卡buffer没调好。你可以先试试把NCCL_IB_TIMEOUT调到30以上,同时加上NCCL_IB_RETRY_CNT=7,这俩组合能解决大部分hang住的情况。另外检查一下两机的ibdev2netdev是否配对正确,有时候交换机端口没走通就会随机超时。如果还不行,把init_method改成tcp://加一个固定IP的端口,别用env://自动发现,我们之前就这么绕过去的。
试试把NCCL_IB_TIMEOUT调到60再关掉NCCL_IB_DISABLE,另外检查下mcp的节点间路由是不是走了别的网卡。
我们之前也遇到过,最后发现是init_method里网卡IP写错了,换成ib0的地址就稳了。
我们之前也踩过类似的坑,MCP集群走IB的话,NCCL_IB_TIMEOUT建议直接拉到30以上,默认值在跨机高负载下确实容易超时。另外检查一下NCCL_P2P_DISABLE和NCCL_SHM_DISABLE这两个变量,有时候自研集群的虚拟化层会干扰P2P传输,强制走IB反而更稳。group初始化方式倒是其次,但记得确认一下每台机器的网卡名称是否一致,我们就是因为节点间ib0和ib1命名不统一导致随机hang。
NCCL超时这个坑我太熟了,尤其是跨节点走IB的时候,十有八九不是PyTorch的问题,是底层通信拓扑没对上。你先别急着调batch size,那个跟梯度同步hang住基本没关系,重点看两件事:一是NCCL_IB_TIMEOUT和NCCL_IB_RETRY_CNT,MCP这种自研集群的IB交换机有可能没开自适应路由,默认超时时间太短,建议直接拉到NCCL_IB_TIMEOUT=22(单位是微秒,别设太小),然后NCCL_IB_RETRY_CNT=7,这俩组合基本能救回80%的偶发超时。
另外你确认下NCCL_SOCKET_IFNAME是不是指向了正确的网卡,InfiniBand环境下经常会有多个接口,如果它选到了管理口或者loopback,通信就会莫名其妙卡住,可以用ip a看看实际IB接口名,比如ib0,然后显式设成NCCL_SOCKET_IFNAME=ib0。还有,如果你们MCP集群有特殊的MPI或集合通信库包装,比如用了自家改的nccl-tests,那最好对比一下原版NCCL的allreduce性能,排除驱动层问题。
初始化group这块,如果你用的是init_process_group,确认下init_method是不是tcp://加一个所有节点都能访问的IP和端口,别用localhost,另外world_size和rank的传递要确保主节点先启动,不然子节点会一直等。最后,实在不行就开NCCL_DEBUG=INFO跑一次,看是卡在recv还是sent,那个日志能精确到哪一步,比瞎猜强多了。三天不亏,我上周调这个调了五天。
之前也踩过类似的坑,多机的时候NCCL_IB_TIMEOUT别用默认值,直接设成60或者120试试,还有NCCL_IB_RETRY_CNT也得调大。另外检查一下两台机器的IB网卡是不是都绑到了同一个subnet,MCP集群经常有路由配置不一致的问题。group初始化的话,建议用init_method="tcp://"加一个单独的master地址,别依赖环境变量自动发现。还有个小技巧,把NCCL_DEBUG=INFO打开看下报错前卡在哪个collective上,大概率是拓扑感知没生效,设置NCCL_TOPO_DUMP_FILE看看。
之前跑过类似的多机DDP,NCCL超时大概率是IB通信和GPU拓扑没对齐,试试把NCCL_IB_TIMEOUT调到30以上,再加个NCCL_IB_RETRY_CNT=7,另外确认下MCP集群的网卡映射是不是走的PCIe直连,vPI隔离层容易引发握手超时。还有个坑是init_method用tcp://时,多机必须指定rank和world_size,建议换成共享文件系统方式初始化,能省掉不少同步问题。
遇到过类似的坑,不过我们当时是IB和RoCE混布,最后问题出在网卡buffer上。你单机没问题但跨节点就超时,大概率不是group初始化的事,DDP那个init_process_group只要rank和world_size对得上基本不会卡在这。建议先看下NCCL_IB_TIMEOUT,默认值在IB网络下经常不够,我们直接调到1800才稳,还有NCCL_IB_RETRY_CNT也顺手设大点。另外有个冷门但很关键的点,检查一下两机的GPU拓扑,如果PCIe switch层级不一样,NCCL会自动选不同的transport,有时候会走成TCP兜底,那速度直接崩盘,你可以设NCCL_DEBUG=INFO看日志里到底用的哪个协议。还有个小技巧,把NCCL_BUFFSIZE调成256MB试试,我们之前调这个解决了偶发hang。如果还不行,看看是不是MCP那边有个共享存储的锁竞争,某些自研调度器会限制IB的并发连接数,这个得找平台组确认。三天不短了,先抓到日志里的关键warning再动手,别瞎试参数。
看到你说单机8卡没问题、两机就挂,我第一反应是觉得问题可能不在梯度同步本身,而在MCP集群的通信拓扑上。InfiniBand虽然快,但多节点时NCCL默认会走IB的GID,你们如果没配好NCCL_IB_GID_INDEX或者NCCL_IB_DISABLE,很容易在握手阶段就超时。我之前遇到过类似情况,最后是强制设了NCCL_SOCKET_IFNAME指向实际网卡,同时把NCCL_IB_TIMEOUT调到22(默认是14左右),一下子稳了很多。另外你调batch size和梯度累积其实对NCCL超时帮助不大,那玩意儿更吃网络带宽和延迟,不如试试NCCL_BUFFSIZE调大点,或者把NCCL_NTHREADS设成256,减少线程切换开销。还有就是你初始化group的方式,如果用的是init_process_group,确认一下init_method是不是走的tcp://,我见过有人用env://在多机下因为rank顺序没对齐导致卡死。最后建议你跑一下NCCL的all_reduce测试脚本,单独测两机通信带宽,能快速定位是硬件问题还是PyTorch层的问题。三天不算啥,我之前调这玩意儿花了快一周,加油。
我也在MCP上踩过类似的坑,当时是NCCL_IB_TIMEOUT默认值太小,InfiniBand稍微有点拥塞就直接超时,你试试把这个调大到60或者90,另外NCCL_SOCKET_IFNAME得明确指定ib接口,别让它自动选。还有个小细节,init_method里的端口号换一个不常用的试试,之前遇到过端口被占导致group初始化卡住的情况。如果还不行,看看两机之间的IB的mtu是不是一致,这个不一致也会引发奇怪的hang。
我之前也被这个坑过,MCP集群多机训练时NCCL的默认超时设置经常不够用,可以试试把NCCL_IB_TIMEOUT调到22以上,另外记得确认一下IB的GID是不是配成了RoCE模式,这俩不匹配特别容易hang。还有,你初始化group的时候用的是init_method=env://还是tcp://,如果有多个网卡的话最好显式指定NCCL_SOCKET_IFNAME和NCCL_IB_HCA,不然它可能选了错误的接口。我们之前还遇到过两机之间MTU不一致导致梯度同步失败,检查一下ibstat和ibstatus输出的MTU值是否都是4092或2048。如果还不行,可以把NCCL_DEBUG=INFO打开,看下它卡在哪个collective上,能更精确锁问题。
NCCL_IB_TIMEOUT调到30以上,再把NCCL_IB_RETRY_CNT设个7,大概率能解决。
我之前也踩过类似的坑,多机扩展时单机没问题不代表NCCL配置就对了。你先试试把NCCL_IB_TIMEOUT调到30以上,还有NCCL_IB_RETRY_CNT别用默认值,这俩在InfiniBand下经常是元凶。另外,检查下两机的网卡速率和MTU是不是一致,不一致也会莫名hang。你初始化group用的是init_method的tcp://还是共享文件系统?我们后来换成了env://反而稳定很多。还有个冷门但有效的招:关掉NCCL的PCI带宽检测,设成NCCL_P2P_LEVEL=LOCAL,能减少跨机通信的隐形开销。