ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NCCL报错ibv_reg_mr_iova2内存注册失败:原因排查与解决方案

NCCL报错ibv_reg_mr_iova2内存注册失败:原因排查与解决方案 跑分布式训练最怕什么不是loss不降而是你已经跑了三个小时日志突然刷出一行NCCL ERROR ... ibv_reg_mr_iova2 failed然后整个训练任务直接崩掉。这时候你连看loss的机会都没有先面对的是全卡退出、模型权重没保存、多机任务被迫重启的局面。如果你用的是一套多机多卡训练环境而且跨节点走的是InfiniBand或RoCE那大概率早晚会遇到ibv_reg_mr_iova2内存分配失败这个问题。它的报错信息一般长这样[0] NCCL INFO NET/IB : Register MR failed : ibv_reg_mr_iova2 returned errno 12 (Cannot allocate memory) [0] NCCL ERROR net_ib.c: 686 - 2第一次遇到这个报错我也下意识觉得是显存或者系统内存不够了于是打开free -g一看明明还有几百GB内存显存也没占满但NCCL就是注册不了内存区域。后来翻了源码、排查了系统配置、在容器和物理机之间反复测才把问题彻底理清楚。这篇文章我整理成3套可落地的解决方案再加一套完整的排查流程希望能帮你少走点弯路。这个问题的本质是NCCL在建立跨节点通信链路时需要把GPU显存映射到RDMA设备比如Mellanox/MCX系列网卡上而映射过程中调用了InfiniBand Verbs层的内存注册接口。注册失败不等于系统真的没内存了更多时候是锁内存限制、物理页可用性、IOVA地址空间寻址能力这几类问题叠加出来的。下面我们一步步来。1. 先说清楚ibv_reg_mr_iova2 到底在忙活什么1.1 从一条报错信息开始ibv_reg_mr_iova2是InfiniBand Verbs API中的一个函数作用是把一段内存区域注册成RDMA网卡可以访问的Memory Region简称MR。注册成功后网卡才能对这段内存做RDMA读写。NCCL在执行AllReduce、AllGather这类集合通信时如果走了IB网络就需要把每个GPU的通信缓冲绑定到网卡上而这个绑定动作就是通过注册MR来完成的。从报错信息看出问题的位置在net_ib.c这个文件这是NCCL源码里处理IB网络的模块。NCCL调用ibv_reg_mr_iova2时期望的返回值是一个MR指针结果返回了错误码2对应errno为ENOENT或者 NDRC错误码同时系统层errno是12也就是ENOMEM。这种情况很奇怪因为应用进程明明还有很多内存可用。1.2 理解MR注册机制注册MR本质上做两件事锁定内存页mlock防止内存被换出到swap。告诉网卡这段物理内存的地址映射关系和访问权限。ibv_reg_mr和ibv_reg_mr_iova2的区别在于后者可以让调用方显式指定IOVAIO Virtual Address也就是网卡访问这段内存时看到的虚拟地址。这个IOVA可以等于CPU的虚拟地址也可以是显存里的GPAGuest Physical Address。在GPUDirect RDMAGDR场景下NCCL会把GPU显存当作目标显存地址要能被网卡直接寻址所以必须用iova2这种支持显存地址映射的接口。所以当你看到ibv_reg_mr_iova2失败时本质上就是网卡侧无法为GPU显存建立可访问的地址映射。映射失败的原因从内核角度看就是ib_umem_get阶段出错需要拿到物理页并锁定。这个动作涉及内存管理子系统和设备驱动对地址空间的校验。1.3 失败时底层发生了什么在调用链上NCCL先通过CUDA驱动拿到GPU显存的设备指针然后把这个指针传给ibv_reg_mr_iova2。驱动层会检查这段地址空间是否已经被其他设备占用、是否有权限映射、是否能锁定足够多的物理内存页。如果失败最可能的是下面几种情况当前进程的RLIMIT_MEMLOCK锁内存上限太小导致ib_umem_get在内核里申请锁定页面时被拒。系统分配了大页内存HugePages但预留量不足或者IOVA映射区与显存BAR地址存在重叠。GPU显存碎片化严重找不到足够大的连续物理空间。网卡驱动或固件版本与CUDA版本不匹配导致GDR映射功能异常。这些原因交叉在一起导致排查起来比较繁琐。最有效的思路是先确认是系统资源问题还是设备映射问题再针对性调整。2. 排查SOP不盲改参数先做一轮系统巡检遇到这个报错后我的第一反应不是直接改NCCL参数而是先做一轮系统巡检把可能的原因一网打尽。下面是我总结的排查流程按照顺序执行一遍基本就能确定问题出在哪一层。2.1 第一步用NCCL_DEBUG拿到关键行先打开NCCL的调试日志让报错信息带上更多上下文export NCCL_DEBUGINFO export NCCL_DEBUG_FILE/tmp/nccl_debug.log重新启动训练任务等报错出现后去日志里搜Register MR failed附近的输出。常见的情况是[0] NCCL INFO NET/IB : Register MR failed : ibv_reg_mr_iova2 returned errno 12 (Cannot allocate memory)如果报错前还有[0] NCCL INFO NET/IB : Using 2 GPUs / 1 NIC / 2 peers说明NCCL已经完成网络设备初始化只是在注册GPU内存时失败了。这能帮我们区分是网卡初始化问题还是内存映射问题。如果日志中出现了proxy thread相关的内容比如[0] NCCL INFO Creating network proxy thread那可以多留意一下proxy线程是否异常退出。NCCL的proxy线程负责管理SEND/RECV buffer的MR注册如果proxy线程在处理MR注册时崩溃也会表现为ibv_reg_mr_iova2失败。2.2 第二步检查内存锁定上限与大页内存在终端里执行ulimit -l cat /proc/meminfo | grep -i hugeulimit -l显示的是当前shell进程能锁定的内存大小。如果输出是一个比较小的数字比如64KB、64MB那基本就可以判定问题是它引起的。因为NCCL注册MR时每个通信buffer可能要锁定数十MB甚至上百MB的内存64MB的限额可能一次注册都不够用。同时查看HugePages情况grep -i huge /proc/meminfo # 重点关注 HugePages_Total 和 HugePages_Free如果系统开了HugePages但预留量很少而NCCL的buffer又落在HugePages区间一样会导致映射失败。2.3 第三步检查RDMA设备与GPU拓扑用以下命令确认RDMA设备正常ibstat | grep -A5 state ibv_devinfo -d mlx5_0 | grep -E state|firmware|base确认网卡状态是ACTIVE固件版本是否过旧。接着用nvidia-smi topo -m查看GPU与网卡的拓扑重点关注PIX、NVLink、NODE等连接关系。如果GPU和IB网卡不在同一NUMA节点GDR性能会受影响但一般不至于导致MR注册失败。如果拓扑显示X或PHBPCIe Host Bridge路径过远可能需要考虑GDR的可用性。2.4 第四步检查驱动、固件和容器配置如果是容器环境先检查启动参数docker inspect container | grep -A10 Ulimits关键看有没有设置memlock-1:-1或者memlockunlimited。很多人在容器里跑训练宿主机内存明明够用但容器默认的memlock值很小导致注册失败。还要确认驱动版本匹配nvidia-smi | grep Driver Version modinfo mlx5_core | grep version rdma-core --versionNCCL、CUDA、驱动、固件四者之间的版本矩阵如果差距过大特别容易出现看似内存问题、实则是驱动能力不匹配的情况。比如NCCL 2.18开始对GDR支持做了更多优化对驱动版本就有更高的要求。2.5 附一键巡检脚本把上面这些检查项写成一个脚本方便在每台机器上快速跑一遍#!/bin/bash echo memlock ulimit -l echo hugepages grep -i huge /proc/meminfo echo ib devices ibstat | grep -E CA name|state|quantity echo gpu topo nvidia-smi topo -m echo nvidia driver nvidia-smi | grep Driver Version echo os info uname -r cat /etc/os-release | grep PRETTY_NAME echo container env cat /proc/1/cgroup 2/dev/null | head -5脚本输出出来后对比正常机器和不正常机器的差异通常能很快定位是环境问题还是配置问题。3. 方案一扩展内存锁定上限并收敛缓冲区占用软件层这是最常用、也最容易被想到的方案。它适用的场景是进程锁内存上限过低导致ib_umem_get在内核态无法锁定足够多的内存页。操作上没有硬件风险改完即可生效适合作为第一优先尝试。3.1 为什么说锁内存是重要原因NCCL在注册MR前需要把将要注册的那段驻留页面锁定在物理内存里。锁定的目的是确保后续RDMA操作发生时网卡访问这段内存的页表项不会变化。内核通过mlock系列的机制实现锁定同时对每个进程设置RLIMIT_MEMLOCK上限。如果这个上限小于NCCL想要锁定的内存大小内核就会拒绝后续的mlock调用反映到用户态就是ibv_reg_mr_iova2返回errno 12。值得留意的是这个限制和系统可用内存大小无关哪怕物理内存还有几百GB空闲只要进程的RLIMIT_MEMLOCK设置太小就会失败。3.2 在宿主机和容器中分别放开限制先确认当前值ulimit -l临时放开只对当前shell会话有效ulimit -l unlimited永久生效编辑/etc/security/limits.conf加入* soft memlock unlimited * hard memlock unlimited注意*在limits.conf里代表所有用户。如果训练任务用特定的用户跑比如root或mluser就单独写root soft memlock unlimited root hard memlock unlimited mluser soft memlock unlimited mluser hard memlock unlimited如果是systemd管理的服务还需要在service文件里加LimitMEMLOCKinfinity容器环境则需要在启动时加参数docker run -ti --ulimit memlock-1:-1 --cap-addIPC_LOCK ...--ulimit memlock-1:-1表示软硬限制都设为无限大。IPC_LOCK权限在某些受限容器里是必须的否则即使memlock设为无限大也可能因为缺少CAP_SYS_RESOURCE而无法锁定内存。3.3 收敛NCCL缓冲区降低单次MR注册压力有些场景下memlock上限已经放开了但仍会注册失败。这时候可能是NCCL默认的通信buffer太大导致单次MR注册请求的连续物理内存过多。NCCL默认buffer size通常是16MB多通道时会为每个channel分配独立的buffer。channel数一多注册总量就会非常大。试着把buffer调小一点减少单次注册的内存需求export NCCL_BUFFSIZE16777216 # 16MB export NCCL_MAX_NCHANNELS2 # 限制最多2个channel如果GPU数量很多比如单机8卡可以进一步限制channel数export NCCL_NCHANNELS2NCCL的channel是并行通信的通道数通道越多通信带宽越高但内存占用也会线性增加。在MR注册失败的场景下先减通道数验证问题如果不再报错再逐步调大找到平衡点。3.4 验证与注意事项配置修改后重启训练任务观察是否还出现ibv_reg_mr_iova2报错。为了快速验证可以用NCCL的测试脚本# 从nccl-tests编译好的all_reduce_perf ./build/all_reduce_perf -b 128M -f 2 -g 8如果这个命令能在多卡上跑通说明MR注册恢复正常。这里有几个容易忽略的细节生产环境中如果通过SSH登录后手动设置ulimit -l unlimited重启训练任务后有效。但如果训练任务是通过systemd、slurm这类任务调度器启动的需要在任务启动脚本里同样显式设置否则会继承一个老的限制值。容器和宿主机是两套限制体系。容器里放开不代表宿主机也放开反之亦然。两边都要设置。检查是否生效进入容器内执行ulimit -l看到unlimited才说明设置成功。4. 方案二治理大页内存与IOVA地址空间系统层如果方案一能解决问题那说明就是简单的锁内存限制问题。但如果memlock已经放开buffer也调小了还是注册失败那么问题可能出在系统和硬件层面大页内存配置不合理、PCIe地址空间不足、或者IOVA映射冲突。这一步需要动系统配置排查起来也更刁钻。4.1 大页配置如何影响MR注册现代服务器CPU和GPU都支持大页内存HugePages比如2MB、1GB大小的页可以减少TLB miss提升内存访问性能。但大页内存对RDMA的MR注册有另一层影响ibv_reg_mr_iova2在映射IOVA时往往需要物理连续或者至少大页对齐。如果系统预留的HugePages不足而NCCL尝试把buffer分配到大页区域就会因为找不够连续大页而失败。检查当前状态cat /proc/meminfo | grep -i huge如果HugePages_Total为0说明没开大页如果为0但程序仍在跑说明NCCL走的是普通4K页一般不冲突。如果HugePages_Total有值但HugePages_Free接近0说明大页被占满了后续任何需要大页的内存映射都会失败。比较麻烦的是即使CXL内存、NVRAM这类设备没用大页某些GPU驱动或网卡驱动在内部可能也依赖大页内存做DMA。所以大页池耗尽的结果就可能表现为RDMA的MR注册失败。4.2 预留和清理HugePages根据系统物理内存大小预留适量大页# 临时预留64个1GB大页立即生效 echo 64 /proc/sys/vm/nr_hugepages1GB大页常用于GPU场景2MB大页则更通用。具体用多大的页取决于GPU驱动、CUDA以及NCCL的内存分配器需求。一般建议在/etc/sysctl.conf里固化为vm.nr_hugepages 2048 vm.nr_overcommit_hugepages 4096vm.nr_overcommit_hugepages是允许系统在运行时临时超预留分配的大页数量相当于一个弹性buffer可以在大页池不足时多分一些出来。如果已经有一部分进程占用了大页需要先确认占用来源cat /proc/meminfo | grep -i huge grep -i huge /proc/pid/smaps | head -20如果发现是其他业务占用的需要协调释放如果只是NCCL自己尝试占用失败可以把HugePages_Free拉高后再试。4.3 调整PCIe资源的再分配另一个容易忽视的点是PCIe地址空间的分配。现代服务器上GPU、IB网卡、NVMe控制器都要映射到PCIe的地址空间而这一段地址空间是有限的。如果PCIe的BARBase Address Register分配不当比如GPU显存BAR和IB网卡的MMIO区域发生重叠或空间不足RDMA就无法完成对GPU显存的映射。此时可以通过内核参数pcirealloc让系统在启动时重新分配PCIe资源编辑/etc/default/grubGRUB_CMDLINE_LINUX... pcirealloc更新grub并重启sudo update-grub sudo reboot重启后用lspci -v | grep -E Region|Memory检查GPU和IB网卡的BAR地址。如果BAR空间明显变大或变得整齐说明之前分配确实存在问题。有些服务器BIOS里也提供了PCIe BAR大小调整的选项从Auto改成2GB或更高可以给GPU和网卡更多映射空间。这个更好因为不用改内核参数直接在重启时进BIOS配置即可。4.4 关闭不必要的P2P或调整BAR大小如果多GPU直连P2P over PCIe使得显存之间互相映射会导致IOVA空间占用成倍增长。这时可以在不影响核心功能的前提下关闭不必要的P2Pexport NCCL_P2P_DISABLE1注意NCCL_P2P_DISABLE1会关闭GPU间的P2P通信NVLink带宽将得不到利用单机8卡场景下性能会明显下降。所以这个配置要谨慎使用仅作为验证手段。如果关闭P2P后MR注册恢复正常说明IOVA空间确实被P2P映射占据了太多需要从BIOS侧扩展BAR空间而不是长期关闭P2P。另外可以检查当前IOVA映射情况确认是否有明显的地址重叠cat /proc/iomem | grep PCI Bus观察GPU、网卡的地址区间是否重叠。如果有重叠或者尾地址接近物理地址空间上限就是典型的IOVA空间不足。4.5 验证与注意事项系统级调整后重启系统让内核参数生效然后重新跑NCCL测试。验证命令和方案一类似但必须确认是在新内核环境下运行uname -a cat /proc/meminfo | grep -i huge cat /proc/iomem | grep -i infiniband\|nvidia再跑一次all_reduce_perf。这个方案的注意事项修改grub前先备份避免启动失败时无法恢复。HugePages预留过多会浪费内存预留过少又起不到作用需要根据训练任务的实际内存需求量估算。一般经验值是预留总内存的20%左右。如果是在云上租的裸金属实例BIOS设置不一定开放这时优先用内核参数pcirealloc或者联系云厂商确认BAR空间大小。5. 方案三临时降级关闭GPUDirect RDMA保住训练有些场景下你不可能立刻重启机器也没时间去调整内核参数。训练任务跑了一半多机同步的shutdown窗口只有几分钟这时候最务实的办法就是降级通信路径绕过GDR让训练先跑起来。5.1 什么时候应该考虑降级当你已经确认了以下事实可以考虑这个方案memlock已经放开buffer已经调小。系统大页配置正常没有明显的内存不足。排除容器权限和驱动版本不匹配的问题。短期内无法重启机器或调整BIOS。也就是说硬件或系统层面的问题一时半会解决不了而训练任务不能长期停摆。这时候通过关闭GDR让NCCL改用host内存作为中转绕过对显存直接MAP到网卡的限制通常能恢复训练。5.2 具体配置方法及配套参数核心环境变量是export NCCL_IB_DISABLE_GDR1这个参数告诉NCCL不要使用GPUDirect RDMA也就是不让IB网卡直接访问GPU显存而是先把数据从GPU拷贝到host内存再由网卡通过RDMA发送。虽然多了一次DMA拷贝但至少能跑通。配套还需要注意以下几个参数避免降级后出现性能雪崩export NCCL_IB_DISABLE_DIRECT_VERBS1 export NCCL_IB_RETRY_CNT7 export NCCL_IB_TIMEOUT22 export NCCL_IB_QPS_PER_CONNECTION2NCCL_IB_DISABLE_DIRECT_VERBS1会让NCCL走内核态的verbs路径而不是绕过内核直接操作硬件这在某些驱动版本下能提高兼容性。NCCL_IB_TIMEOUT22和NCCL_IB_RETRY_CNT7是相对保守的网络超时和重试配置可以降低长尾通信导致的任务中断概率。如果你的机器同时有多张IB网卡还可以显式指定用哪张export NCCL_IB_HCAmlx5_2,mlx5_35.3 降级后的性能表现与缓兵之计关闭GDR后单次AllReduce的耗时通常会上升百分之十几到几十具体取决于数据量和网络带宽。比如原本100GB的AllReduce需要60秒关闭GDR后可能要75秒左右但训练任务能稳定跑下去。相比之下任务因为MR注册失败直接崩掉再重启的代价通常远大于这百分之几十的带宽损失。所以我的建议是把关闭GDR当作“止血方案”而不是长期方案。训练任务恢复后再找窗口期去解决系统层的根因也就是方案二里提到的IOVA空间、大页配置这些。具体操作节奏在启动脚本里临时加上NCCL_IB_DISABLE_GDR1。确认训练稳定运行loss正常下降。计划一次窗口期把第4节的系统层配置修好。重启后关闭NCCL_IB_DISABLE_GDR1或改成0验证能否恢复GDR的高效通信。如果恢复GDR后MR注册再次失败说明根因还没解决需要继续排查硬件/驱动层面的问题。5.4 验证与注意事项降级后必查的点确认NCCL日志不再出现MR注册错误。确认NVLink仍然正常因为NCCL_IB_DISABLE_GDR只影响IB路径的GDR不影响GPU间NVLink的P2P通信。观察训练速度如果因为关闭GDR导致通信成为瓶颈可以适度加大batch size或梯度累积摊薄通信开销。这里要特别提醒一点不要在一台机器上测试降级方案后就直接在所有节点上批量开启关闭GDR的配置。先在一对节点上验证稳定性再看全集群的效果。因为关闭GDR可能影响通信拓扑选择如果某台机器的网络路径因为GDR关闭而变得不均衡其他节点也会被动受影响。6. 三种方案如何选经验优先级的建议三种方案并不是完全互斥的实际排障时往往要组合使用。我根据自己的实践经验整理了一个简单的选型表方案适用场景改动范围性能影响实施难度方案一调memlock 收敛buffermemlock限制过低、buffer占用过大纯软件配置立即生效无明显影响低方案二治理大页与IOVA空间大页池不足、PCIe BAR空间冲突内核参数、BIOS、重启恢复后无额外性能损耗中高方案三关闭GDR降级无法及时重启应急恢复训练环境变量立即生效有额外拷贝开销性能下降低我在处理这类问题时决策顺序一般是先看日志确认是不是memlock相关的报错。如果是直接方案一。如果方案一无效查看大页和IOVA状态尝试方案二。如果生产任务不能等立刻方案三止血回头再补方案二的根因修复。从经验上看单机8卡以上、多机使用IB网络的场景最常见的坑还是memlock而双机或多机且使用了较大batch size、较大buffer的场景更容易踩到IOVA空间的问题。尤其是当GPU显存容量很大比如40GB、80GB时NCCL默认会为每个channel预留大量bufferIOVA总占用会迅速膨胀。另外一个值得注意的现象是这类问题在NVIDIA H100/A100平台上的表现可能和老一代V100平台不同。新平台对GDR的支持更完善但同时也对驱动版本、固件版本更敏感。如果你用的是老驱动新NCCL或者新驱动在CUDA 11.2上调试NCCL 2.19都容易出现MR注册失败。7. 几个容易让人绕路的细节这部分是我踩坑踩出来的经验单独拎出来说说。第一systemd环境和SSH会话的ulimit不是一回事。很多人修改了/etc/security/limits.conf但训练任务是由systemd服务拉起时发现依然受限制。这是因为systemd会忽略limits.conf只认service文件里的LimitMEMLOCK。如果你是用systemd管理训练进程一定要记得在service文件里写LimitMEMLOCKinfinity。第二多机集群的配置必须同步。这个问题看起来简单实际特别容易翻车。你在一台节点上调好了memlock但其他节点没调训练任务启动后NCCL会在通信初始化阶段全集群同步状态只要有一台机器的MR注册失败整颗集群的训练都会失败。所以在修改任何环境配置时记得同步到所有机器上或者直接写进入口启动脚本。第三注意NCCL_debug日志的行号变化。不同NCCL版本中net_ib.c的行号不同如果你看到日志中报错行号不同不要奇怪它只是版本差异。关键是看errno是多少。errno 12是ENOMEMerrno 22是EINVALerrno 1是EPERM这三者对应完全不同的排查方向。如果是EPERM更多是权限问题优先看容器的capabilities尤其是IPC_LOCK。第四NCCL源码里ibv_reg_mr_iova2的调用点通常在ncclIbMrReg或类似函数中它会把MR注册请求提交给proxy线程处理。所以如果你看到proxy线程崩溃或者卡住也可以顺藤摸瓜找到MR注册失败的根因。分布式训练环境越复杂越建议把NCCL升级到较新版本新版本在错误上报、调试信息上都要友好得多。第五有些时候问题不是NCCL自身而是底层通信库和固件的问题。升级Mellanox固件前先确认兼容性最好是和NVIDIA驱动一起升级不要只升其中一个。固件和驱动版本不匹配时即使内存配置完全正常MR注册也可能失败。这个情况我踩过一次折腾了大半天最后升级了固件才解决。最后再分享一个小技巧。遇到MR注册失败时可以在NCCL的启动脚本里加上export NCCL_DEBUG_SUBSYSNET,IB export NCCL_DEBUG_LEVELDEBUG这个组合会输出更细粒度的IB通信日志包括MR注册前后的详细状态。日志量会很大但排查MR问题非常有效。排查完记得关掉否则后续训练日志会刷得停不下来。分布式训练的坑千千万MR注册失败只是其中一个。但这类问题一旦出现训练任务基本都会中断影响面很大。希望这篇文章里的排查流程和三个方案能帮你在下次遇到时少走弯路也能在团队里快速定位问题边界。
返回列表