最近在折腾一个多节点分布式训练的任务,用的 PyTorch DDP,后端是 NCCL。环境是公司自研的 MCP 集群,网络走的是 InfiniBand。单机 8 卡跑没问题,但一扩展到两机 16 卡就频繁报“NCCL 超时”和“梯度同步失败”。我检查了网卡配置和 NCCL 版本,也试过调小 batch size 和梯度累积步数,还是偶尔会 hang 住。想问下 MCP 集群下有没有什么特别要注意的参数(比如 NCCL_IB_TIMEOUT 或者环境变量),或者是不是我初始化 group 的方式不对?真心被这个问题卡了三天了,求过来人指条明路。
MCP 环境下用 PyTorch 做分布式训练,梯度同步总是报错,有大佬指点吗?
全部回复
共 114 条NCCL超时八成是IB网络拓扑没对齐,试试把NCCL_IB_TIMEOUT调大,另外确认下MCP的共享内存和网卡绑定对不对。
我之前在类似环境也踩过这坑,NCCL超时大概率不是batch size的问题,而是IB通信的握手阶段没调好。你可以先试试显式设NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,这两个值在跨机场景下很关键。另外检查下NCCL_SOCKET_IFNAME是否指向了正确的IB接口,有时候多网卡会导致选错设备。group初始化本身一般没问题,但建议确认下init_method用的TCP端口在两台机器上都放行了,我们当时就是被防火墙卡了三天。
我之前也踩过类似的坑,多机情况下NCCL超时大概率不是batch size的问题,而是IB网卡和NCCL版本匹配度不够。你可以先试试显式设置NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,再把NCCL_DEBUG=INFO打开看具体卡在哪个rank的allreduce上。另外确认一下init_method用的是tcp://还是file://,MCP环境里如果共享文件系统可用,用file有时候比tcp稳。还有个小细节,检查一下两台机器的GID索引是否一致,这个经常被忽略但特别容易导致hang。
NCCL_IB_TIMEOUT调大点试试,另外检查下MCP集群的共享内存和网卡队列深度,之前我们也是被这个坑了。
两机16卡这种跨节点场景,NCCL的IB超时确实是个大坑,默认值在InfiniBand下经常不够用。你可以先试试把NCCL_IB_TIMEOUT调到30以上,同时把NCCL_IB_RETRY_CNT设成7或更高,这俩组合拳能解决大部分hang住的情况。另外确认下MCP集群有没有禁掉GDR(GPU Direct RDMA),有时候自研平台会默认关掉这个,导致跨机通信走内存拷贝,带宽骤降就容易超时。group初始化方式一般没问题,但建议把init_method改成tcp://并指定一个空闲端口,避免用env方式在多机下抢端口。最后检查一下两台机器的网卡速率和MTU是否一致,不一致也会引发偶发同步失败。
遇到过类似的坑,不过我们当时是千卡集群,问题更诡异。你单机8卡没问题,两机就挂,大概率不是模型或超参的事,先别调batch size了,纯粹是通信层的问题。NCCL_IB_TIMEOUT确实要设,但很多人忽略的是NCCL_IB_RETRY_CNT,默认值在IB网络下偏小,超时重传次数不够就直接hang。还有NCCL_SOCKET_IFNAME,如果机器上有多个网口(比如管理网和IB网共存),不强制指定的话NCCL可能走了慢速网络,这个排查起来最隐蔽。另外你们MCP集群如果是自研调度器,检查下容器内是不是映射了完整的IB设备,有时候/dev/infiniband只挂了一部分,NCCL初始化时检测不到就静默降级了。group初始化方式反而问题不大,DDP默认的env初始化在MCP下够用,除非你们有特殊的节点拓扑,否则不用改。最后建议你开一下NCCL_DEBUG=INFO,看看到底卡在哪个stage——是allreduce还是broadcast,日志里会明确告诉你哪一步超时,比盲调参数高效得多。
试试把NCCL_IB_TIMEOUT调到30以上,另外检查下MCP的共享内存和IB的GID配置,之前我们就是这么解决的。
我们之前也遇过类似情况,最后发现是MCP里默认的NCCL_SOCKET_IFNAME没指向IB网卡,手动指定一下就好了。
试试NCCL_IB_TIMEOUT调到30以上,还有把NCCL_DEBUG设成INFO看下具体卡在哪个rank上。
试试把NCCL_IB_TIMEOUT调到30以上,再检查下MCP的共享内存是不是设小了,之前我们就是这么解决的。
看到你这个情况我太有共鸣了,之前我在类似集群上调DDP也是被NCCL超时折磨得够呛。你检查了网卡和版本,但我觉得MCP这种自研环境里最坑的反而是默认的NCCL环境变量没跟上InfiniBand的实际拓扑。建议你先把NCCL_IB_TIMEOUT调大试试,比如设成22或者更大,默认值在跨机高负载下真的不够用,另外NCCL_IB_RETRY_CNT也顺手设个7,这俩组合起来能解决不少偶发hang的问题。
还有个小细节,你初始化group的时候如果用了init_method="env://",记得确认每台机器上的MASTER_ADDR和MASTER_PORT环境变量是全局导出的,特别是用MPI或Slurm启动时,经常会有子进程没继承到导致握手卡死。另外可以开一下NCCL_DEBUG=INFO跑一次,看日志里是卡在ring setup阶段还是数据传输阶段,定位会准很多。
我上次还发现一个坑是IB的GID索引需要显式指定,有时候默认选错端口会导致超时,你可以在启动脚本里加NCCL_IB_GID_INDEX=3(或者根据ibstat查一下实际值)试试。如果还不行,试试把梯度累积和batch size调回去,用原始的配置加NCCL_ASYNC_ERROR_HANDLING=1,这样报错时不会直接hang而是抛异常,能更快看到是哪个rank出了问题。最后问下你用的PyTorch版本是2.1以上吗?老版本对多节点IB的支持有些已知bug,升个级可能也顺手解决了。
遇到过类似的情况,不过我们是在普通以太网+RoCE上踩的坑,但思路应该差不多。你单机8卡没问题,说明代码逻辑和GPU通信本身没毛病,问题几乎肯定出在跨节点的那条链路上。NCCL超时本质就是等待同步的rank没在规定时间内收到数据,你先别急着调batch size,那个大概率是干扰项。建议直接把NCCL_DEBUG=INFO打开跑一次,看日志里到底是卡在ring setup阶段还是实际数据传输阶段,这两个的排查方向完全不同。如果是setup阶段报错,多半是init_process_group里传的world_size或者rank跟实际节点数对不上,或者MASTER_ADDR只写了第一个节点的IP但没保证所有进程都能访问;如果是传输阶段超时,那就要查InfiniBand的MTU和GID是否一致,特别是MCP这种自研集群,有时候驱动默认参数跟NCCL的预期不匹配。另外NCCL_IB_TIMEOUT这个值不是越大越好,设成22(约10秒)就够用了,再大反而会掩盖底层问题,让hang住的时间变长。还有个冷门但有效的招:试试把NCCL_SOCKET_IFNAME显式指定成ib0或者你实际用的网卡名,有时候默认选错设备会走回TCP兜底,性能骤降且容易超时。最后问一句,你们MCP集群的节点间是不是有防火墙或者安全组策略?我之前就遇到过跨节点端口被拦导致初始化握手卡死,但单机完全正常的情况。
我们之前也踩过类似的坑,MCP集群的IB网卡虽然快,但默认的NCCL超时设置经常不够用,你可以先试下把NCCL_IB_TIMEOUT调到22以上(单位是us),顺手把NCCL_SOCKET_IFNAME指到具体的ib接口,别让它走docker0。另外group初始化建议用init_method="tcp://"加一个固定的master地址(最好是IB地址),别用env://,多节点下env传参偶尔会丢rank。如果还hang,可以开NCCL_DEBUG=INFO看卡在哪个集合通信上,通常要么是共享内存段太小,要么是IB的queue pair数不够,这时候调NCCL_IB_QPS_PER_CONNECTION=8能救一下。还有个小细节,检查下每台机器的ulimit -l(锁内存)是不是unlimited,这个我见过导致随机超时的。
我之前也踩过类似的坑,两机互联时NCCL超时八成不是batch size的问题,建议先查一下NCCL_IB_TIMEOUT和NCCL_IB_RETRY_CNT,InfiniBand下默认值偏保守,调大点能缓解偶发hang。另外确认下init_method用的是tcp://还是file://,MCP集群里如果节点间有防火墙,tcp那套容易卡握手。还有个小细节,torch.distributed.init_process_group里backend='nccl'时,最好显式指定init_method的端口别跟别的服务冲突,我之前就是端口被占导致同步崩。你试过用NCCL_DEBUG=INFO跑一次吗?日志里能看到具体是哪个环或树算法卡住,比盲调变量靠谱。
我之前也踩过类似的坑,MCP集群上NCCL超时大概率不是batch size的问题,而是IB通信拓扑没对齐。建议先确认一下两台机器的网卡是否都绑到了同一个subnet,然后显式设一下NCCL_IB_TIMEOUT=22和NCCL_IB_RETRY_CNT=7,这两个值在IB链路上很关键。另外你的init_method如果是tcp://,试试换成共享文件系统的方式,有时候IP直连在跨节点会触发奇怪的握手延迟。还有个小细节,DDP初始化时最好把device_ids显式传进去,别依赖默认行为,我之前就是这么莫名其妙修好的。
试试把NCCL_IB_TIMEOUT调到60,再加NCCL_IB_RETRY_CNT=7,我们之前就是这么救回来的。
之前遇到过类似的,NCCL超时大概率不是batch size的问题,先把NCCL_IB_TIMEOUT调到60以上试试,还有NCCL_IB_RETRY_CNT也得设个7或者8。另外检查下两机的IB网卡是不是都绑到了正确的NUMA节点上,MCP集群如果跨机走的是虚拟化overlay,可能还得看下NCCL_SOCKET_IFNAME指向的是不是实际IB接口。还有个坑是init_method用的TCP还是共享文件系统,两机之间如果文件系统不同步也会hang。
NCCL_IB_TIMEOUT调到30以上试试,另外确认下两台机器的IB网卡是否都在同一子网,之前我卡这问题就是网段隔离了。
之前调A100集群也踩过这坑,NCCL超时大概率不是batch size的问题,你先试试把NCCL_IB_TIMEOUT调到30以上,还有NCCL_IB_RETRY_CNT设个7,这两个对IB网络抖动特别管用。另外确认下两机的GPU拓扑是不是对称的,MCP集群有时候交换机路由不一样会导致跨节点握手慢。group初始化的话,记得用init_method="env://"并且每台机器都显式指定MASTER_ADDR和MASTER_PORT,别依赖默认值。如果还hang,开NCCL_DEBUG=INFO看下卡在哪个环路上,一般能定位到是哪条链路的问题。
之前调过类似的InfiniBand多机DDP,NCCL超时大概率是IB的GID或者MTU没对齐,先确认下ibstatus看两机速率是不是一致,然后试着把NCCL_IB_TIMEOUT调到30以上,同时NCCL_IB_RETRY_CNT设个7或8。另外你初始化group用的init_method是tcp还是共享文件?MCP环境里如果host网络有防火墙,tcp端口没放通也会卡在同步阶段。我之前还遇到过因为OMP_NUM_THREADS设太大导致和NCCL抢CPU资源,把每个进程的线程数限到4以下就好很多。
碰到这种两机就挂单机没事的情况,我第一反应就是IB通信和NCCL的拓扑感知没对上。你试试设NCCL_IB_DISABLE=0然后显式指定NCCL_IB_TIMEOUT=22,默认的timeout经常在长消息同步时不够用,尤其MCP这种共享集群可能有别的任务抢占带宽。另外别忘了NCCL_SOCKET_IFNAME要指到ib接口,有时候默认选了docker0或者管理网卡导致握手慢半拍。初始化group那边,你确认一下用的init_method是tcp://还是file://,MCP环境下如果共享文件系统有延迟,file方式容易导致rank不同步,建议换成tcp。还有个小坑,检查一下两机的GPU topology是不是对称的,NCCL会按rank顺序做ring-allreduce,如果PCIe switch拓扑不一致,跨机通信路径会绕远。最后实在不行可以开NCCL_DEBUG=INFO把日志拉出来看下是卡在allreduce还是broadcast,定位到具体原语之后再针对性调参。希望能帮到你,这种问题排查起来确实磨人。