项目介绍
基本介绍
NVIDIA Resiliency Extension简称NVRx( https://github.com/NVIDIA/nvidia-resiliency-ext ),是NVIDIA面向大规模PyTorch分布式工作负载开发的一组训练韧性组件。它关注的不是让单个训练步骤计算得更快,而是通过更早发现故障、更快恢复训练、更频繁地保存状态以及定位慢rank,提高集群真正产出有效训练进度的比例,也就是训练goodput。
NVRx不是训练框架、作业调度器或检查点存储系统。它不会替代PyTorch DDP、Megatron-LM、NeMo、SLURM或共享存储,而是以Launcher、Python API、函数包装器和可选回调等形式嵌入这些系统之间。其主要价值可以概括为:
- 在既有资源分配内检测
rank失联或卡死,并重新拉起训练进程; - 在条件允许时直接在同一操作系统进程中重进训练函数,进一步减少初始化开销;
- 将耗时的检查点写入移到后台进程,并支持节点本地介质与副本;
- 对训练代码段和
CUDA Kernel计时,识别拖慢同步训练的rank; - 在恢复边界执行
GPU、NVLink、NIC和存储健康检查,汇集日志及PyTorch Flight Recorder信息,辅助故障归因。
| 项目属性 | 当前信息 |
|---|---|
| 源码仓库 | NVIDIA/nvidia-resiliency-ext |
| 主要实现 | Python,慢rank分析包含C++/CUPTI扩展 |
| 开源协议 | Apache License 2.0 |
官方支持的GPU平台 | 当前仅面向NVIDIA GPU,官方环境要求包含CUDA、NVML和NCCL,未声明支持AMD/ROCm等其他加速平台 |
| 稳定性定位 | 项目README标注为实验项目并处于活跃开发期 |
它解决了哪些训练痛点
大模型同步训练的故障成本会被集群规模放大:一个rank卡死会让其他rank阻塞在集合通信上,一次检查点写盘会让大量GPU一起等待,一块降频或异常的GPU则可能持续拖慢整个数据并行组。NVRx将这些问题拆成检测、恢复、状态保存和诊断四类能力。
| 现实痛点 | NVRx如何解决 |
|---|---|
训练任务hang死或局部节点故障时,整个作业直接崩溃,需要从头重新调度;这既可能损失数小时训练算力,重新申请大批节点也往往耗时很久 | In-job Restart不销毁已有的SLURM资源分配,直接在现有节点上重新拉起训练进程并恢复;In-process Restart还能在健康rank的同一操作系统进程内重进训练函数,进一步省去容器、Python进程、依赖和CUDA Context等重建开销,缩短故障恢复时间 |
传统同步Checkpoint在保存断点时,会让全部训练进程停下来等待写盘;大模型检查点体积巨大,一次写盘可能耗时数分钟,期间大量GPU算力空转 | 异步Checkpoint将实际持久化交给torch.multiprocessing后台进程,训练循环完成必要的快照准备后即可继续执行;本地分层Checkpoint把高频断点写入节点SSD或RAM Disk,并可配置副本,从而减少共享存储I/O压力和前台等待时间 |
集群中个别GPU或节点性能变差,形成慢节点Straggler,会拖累整个同步训练集群的吞吐,但很难发现究竟是哪台机器在拖后腿 | Straggler Detection自动采集各rank的代码段耗时和可选CUDA Kernel耗时,计算相对性能与个体历史性能分数,识别并报告慢rank,帮助运维人员定位可疑机器 |
大规模训练崩溃后,很难区分故障来源:究竟是GPU硬件、NVLink、网卡NIC、集合通信,还是训练软件本身的问题 | 健康检查通过NVML、NVLink状态和错误计数、InfiniBand NIC链路计数等信号检查硬件;分布式日志和PyTorch Flight Recorder分析收集跨rank故障上下文,实验性的故障归因模块进一步给出证据与重启或停止建议,辅助而不是替代最终根因判断 |
PyTorch原生分布式容错能力较弱,用户需要自行实现hang检测、快速重启、高效断点和故障诊断逻辑,开发与验证工作量很大 | 项目提供完整的Python API、ft_launcher、inprocess.Wrapper、检查点管理器和慢rank检测组件,可以模块化接入现有PyTorch DDP训练代码;项目也包含PTL回调,但该集成在当前主分支已经废弃,不应作为新项目的长期接口 |
总体架构
理解NVRx之前,可以先把分布式训练想象成一个多人协作的流水线:同一份训练任务被拆给许多进程,每个进程通常使用一块GPU。大家必须频繁交换计算结果,因此只要其中一个进程卡住,其他进程往往也只能等待。
后文会反复使用几个分布式训练中的常见概念:
| 概念 | 通俗解释 |
|---|---|
节点(node) | 一台服务器。一台服务器上通常装有多块GPU,所以会运行多个训练进程 |
训练进程(worker) | 真正执行模型训练的操作系统进程,通常一个进程使用一块GPU |
rank | 训练进程的编号,例如rank 0、rank 1。它不是一种特殊组件,可以把它理解为进程的工号 |
WORLD_SIZE | 本轮参与训练的进程总数。例如WORLD_SIZE=8表示共有8个训练进程 |
集合通信(collective) | 多个训练进程共同参与的数据交换,例如汇总所有GPU算出的梯度。少一个参与者,其他参与者就可能一直等待 |
检查点(checkpoint) | 训练过程的存档,通常包含模型参数、优化器状态和当前训练进度。发生故障后可以读档继续训练 |
在这个流水线外,NVRx增加了三类辅助能力:
- 监控与重启:观察每个训练进程是否还在工作,发现异常后重新启动;
- 训练存档:更快、更频繁地保存检查点,尽量减少故障造成的进度损失;
- 观察与诊断:寻找速度异常的进程,收集硬件状态和日志,帮助判断故障原因。
核心运行与重启视图:
最外层的集群调度系统负责分配服务器和GPU。拿到资源以后,每台服务器运行一个NVRx节点启动器,源码中的名称是ft_launcher。它负责启动本机的训练进程,并为每个训练进程配一个独立的监控进程Rank Monitor。监控进程不参与模型计算,只观察对应训练进程有没有定期报告进度。
不同服务器上的启动器还需要通过一个协调服务交换信息。源码把这个会合过程称为rendezvous,意思是让仍然可用的节点确认“哪些成员参加下一轮训练”。其底层保存协调信息的服务常用TCPStore。初次阅读时不必深究这两个名称,只需知道它们共同解决“多台服务器如何就新一轮训练达成一致”即可。
发生故障时有两种恢复力度:
- 作业内重启(In-job Restart):保留调度系统已经分配的服务器和
GPU,只重新启动训练进程; - 进程内重启(In-process Restart):连操作系统进程也尽量保留,只退出并再次执行其中的训练函数。
第二种方式速度可能更快,但对训练代码的要求也更严格,因此是可选能力。
状态保存与观测诊断视图:
这里最容易产生的误解是“进程重新启动后,训练就会自动接着跑”。实际上,重启和读档是两件事:NVRx的启动器只负责让训练代码重新运行;训练代码仍要在启动时主动查找并加载最新检查点,恢复模型、优化器和数据读取位置。没有正确的检查点加载逻辑,进程虽然重启了,训练却可能从头开始。
这些能力可以分别使用。例如,只想发现训练卡死,可以仅接入启动器和监控客户端;只想排查哪块GPU拖慢训练,也可以只接入慢进程检测。
功能特性
两级重启机制
作业内重启:保留服务器,只重启训练进程
作业内重启(In-job Restart)适合解决这样的情况:调度系统分配的服务器大多仍然可用,但某个训练进程已经崩溃或卡住。如果直接结束整个作业,不仅会丢失尚未保存的训练进度,还要重新排队申请服务器。NVRx因此选择保留现有资源,仅结束本轮训练进程,然后在这些资源上启动新一轮训练进程。
执行这项工作的程序叫ft_launcher,它是在PyTorch标准启动工具torchrun的基础上扩展而来的。每台服务器运行一个启动器,每个训练进程都有一个Rank Monitor监控进程。两者通过本机进程间通信(IPC)交换状态;不同服务器的启动器则通过前文介绍的协调服务达成一致。监控进程彼此不直接通信。
训练进程可以用两种方式证明自己还在正常工作:
| 检测方式 | 工作方式 | 取舍 |
|---|---|---|
心跳(Heartbeats API) | 训练循环定期发送“我还在工作”的信号 | 接入简单,但超时时间必须大于一次正常的数据加载、评估或检查点保存时间,否则容易误报 |
代码段计时(Sections API) | 分别记录“开始执行前向计算”“前向计算结束”等事件 | 需要改动更多训练代码,但能针对不同阶段设置不同的超时时间,更快定位卡在哪一步 |
一次典型恢复可以概括为:监控进程发现某个训练进程长时间没有进展,启动器结束本轮所有训练进程,各节点重新确认参与者,然后启动下一轮训练。新进程最后从检查点恢复状态。
当前版本采用“一个失败,全部重启”的规则。这是因为集合通信要求所有参与者处于一致的训练轮次,只替换其中一个进程很容易造成状态不一致。参数--max-restarts用于限制一个作业最多可以重启多少次,避免无法修复的故障导致无限循环。
主分支还提供热备服务器、按照NVLink连接范围重新分配进程编号、检测反复无进展等高级能力。这些功能变化较快,生产使用时应以固定版本的文档为准。已经废弃的--ft-restart-policy参数也不应再用于新配置。
再次强调,ft_launcher只负责重新启动进程。模型参数、优化器状态、学习率调度器、随机数状态和数据读取位置,仍需训练程序从检查点正确恢复。
进程内重启:保留进程,重新执行训练函数
进程内重启(In-process Restart)比作业内重启更进一步。它尽量不结束Python进程,而是从当前训练函数中退出,清理已经失效的分布式通信环境,再调用一次训练函数。这样可以省去重新启动容器、加载Python依赖和初始化CUDA运行环境等开销。
项目通过inprocess.Wrapper包装需要重复执行的训练函数。发生未处理异常或长时间没有进展后,它会在仍然健康的进程中协调完成以下步骤:
- 终止旧的分布式通信组,并退出本轮训练函数;
- 执行用户配置的清理逻辑(源码称为
Finalize),同时检查本机硬件是否健康; - 排除已经终止、失联或不健康的进程,为剩余进程重新编号,并更新参与训练的进程总数;
- 执行初始化逻辑(源码称为
Initialize),再次调用训练函数。
最简单的接入方式是包装整个main()函数;更细致的方式是只包装依赖分布式通信的训练循环。后者可以保留与通信无关、能够安全复用的对象,但也更考验开发者对对象生命周期的理解。
这项能力的限制比普通进程重启更多:
- 被包装的函数必须允许重复调用,第二次执行不能依赖第一次遗留的临时状态;
- 保留下来的对象不能继续引用已经失效的分布式通信组;
- 与分布式通信有关的模型和优化器状态,恢复时最好从检查点重新加载;
- 训练函数不能捕获并忽略包装器用于退出当前训练的
BaseException; - 如果某个底层操作长时间独占
Python GIL,负责恢复的线程无法运行,NVRx最终只能强制结束整个进程。
默认情况下,编号为0的进程还承载内部协调服务TCPStore。如果它所在的服务器彻底失联,整个作业仍会结束,除非项目另行实现并配置其他协调服务。
官方文档明确说明,作业内重启与进程内重启如何组合仍在重新评估。源码中虽然已有两级恢复相关实现,但新系统不能因此假设它已经是稳定接口;正式采用前需要模拟进程崩溃、通信卡死和节点失联等故障进行验证。
检查点体系
异步检查点
检查点类似于训练过程中的游戏存档。传统的同步保存方式会暂停训练,依次整理模型状态、把数据从GPU复制到主机内存、转换成可保存的格式,最后写入磁盘。分布式训练还要等待所有进程都保存完成。在这个过程中,昂贵的GPU可能一直空闲。
异步检查点的思路是缩短这段暂停时间:训练进程只准备一份状态快照,然后把耗时的复制、转换和写盘工作交给后台进程,自己尽快继续下一步训练。后台保存和前台训练可以在一段时间内并行进行。
在源码中,一次待执行的后台保存任务叫AsyncRequest,多个任务由AsyncCallsQueue排队管理。默认实现PersistentAsyncCaller会创建一个长期运行的后台进程,不必每次保存都重新创建进程。它可以通过进程间共享机制访问GPU张量,先把数据复制到主机内存,再完成实际写盘。
项目提供两种常用入口:
TorchAsyncCheckpoint:在后台执行普通的torch.save保存;save_state_dict_async_plan与FileSystemWriterAsync:在后台保存分布式检查点,并缓存重复使用的保存计划,减少后续保存前的协调工作。
训练过程中可以调用maybe_finalize_async_calls(blocking=False)查看后台任务是否完成,但不停止训练;训练结束前则用blocking=True等待所有保存任务真正完成。最后还要调用close()关闭后台进程,否则可能遗留子进程或未提交的任务。
异步保存并不是“免费”的。生成一致的快照、把张量复制到主机内存,以及必要的GPU同步,仍会让前台短暂停顿;后台进程也会占用CPU、内存和存储带宽。它只是让写盘与后续训练尽量重叠,并没有消除写盘成本。尤其要关注主机内存容量,避免大检查点与训练进程争抢内存。
本地检查点与副本
大型分布式检查点通常会被拆成多个分片,每个训练进程保存其中一片。把所有分片频繁写入共享存储,容易产生网络和存储压力;写到当前服务器的SSD或内存盘则通常更快。LocalCheckpointManager负责管理这种本地检查点,包括保存分片、寻找最新的完整版本和加载分片。
但本地保存有一个明显风险:服务器发生故障时,它磁盘上的检查点分片也可能无法访问。为降低风险,NVRx可以把一个进程的分片再复制给其他进程。恢复时,如果本机分片丢失,就从仍保有副本的进程取回。
源码中,BasicTensorAwareStateDict把普通的训练状态整理成便于复制张量的结构;CliqueReplicationStrategy负责安排副本放在哪里。replication_factor表示每个分片一共保留多少份,值越大越可靠,但占用的内存、网络和存储也越多。replication_jump用于控制副本进程编号之间的间隔,通常要结合服务器与GPU的分布来配置。
本地检查点有明确边界:
- 它适合在同一次作业的短暂故障后快速恢复,不能替代共享存储或对象存储中的长期检查点。整批服务器或本地磁盘同时丢失时,本地副本也会丢失。
- 所有训练进程必须一起调用
save()、load()和find_latest()。如果只有部分进程进入这些操作,其他进程可能一直等待,甚至因张量分配不一致而耗尽显存。 - 使用
BasicTensorAwareStateDict时,状态字典中的张量应位于CUDA设备,并且只能嵌套在字典或列表中。更复杂的数据结构需要用户实现TensorAwareStateDict接口。 - 异步保存本地检查点时,当前版本必须使用
AsyncCallsQueue(persistent=False),即每次保存使用临时后台进程。这是因为部分本地保存函数不能发送给长期后台进程。 - 如果保存时创建了副本,恢复时应使用相同的训练进程总数、副本数量和副本间隔,否则系统可能无法重新拼出完整检查点。
因此,更实用的方案通常是两层存档:频繁写入速度快的本地存储,同时隔一段较长时间再写一份共享存储或对象存储。少量进程或服务器故障时优先从本地恢复;整个作业失效或重新申请资源后,再从长期存档恢复。
NVRx不会自动决定该读取哪一层,选择逻辑需要由训练框架或用户代码实现。源码中曾用于自动选择的HierarchicalCheckpointIO属于已废弃的ptl_resiliency接口,不适合作为新项目的长期依赖。
慢训练进程检测
在同步分布式训练中,各进程需要在关键步骤交换结果。例如有八个训练进程,即使七个进程已经完成,只要第八个还没完成,前七个也只能等待。因此,一块降频的GPU、一台负载过高的服务器或一个异常缓慢的数据读取进程,都可能降低整个集群的速度。项目把这种明显落后的进程称为Straggler,也就是“掉队者”。
慢进程检测(Straggler Detection)允许用户标记需要测量的代码,例如数据加载、模型前向计算或反向传播。它可以收集两类时间:
CPU时间:从代码段开始到结束,现实世界实际过去了多久,其中也包含等待时间;GPU时间:可选地统计这段代码中各个CUDA Kernel在GPU上执行的时间。CUDA Kernel可以简单理解为提交给GPU执行的一小段计算程序。
组件会为每个训练进程计算0.0到1.0之间的性能分数,越接近1.0表示表现越接近参考水平:
| 分数 | 参考基线 | 含义示例 |
|---|---|---|
| 相对性能分数 | 当前作业中最快的训练进程 | 0.5表示该进程的速度大约只有本轮最快进程的一半 |
| 个体性能分数 | 该进程自己过去的最佳表现 | 0.5表示该进程当前速度大约只有自身最佳速度的一半 |
当分数低于用户设置的阈值时,可以通过Report.identify_stragglers()列出可疑进程。相对分数适合发现“某台机器比同伴慢”;个体分数适合发现“这台机器比自己以前慢”,也能辅助判断整个集群是否同时退化。
需要注意,这项功能只能说明“谁比较慢”,不能直接解释“为什么慢”。例如,编号为0的进程如果本来就额外负责保存日志,它自然可能比其他进程慢,并不一定代表硬件故障。
计算相对分数和汇总结果本身也需要进程间通信,因此不宜每一步都生成报告,通常应设置为分钟级。官方文档称GPU计算分析一般预期增加不到1%的单步耗时,但实际开销与模型和采样频率有关,仍需在真实训练任务中测量。
健康检查、日志与故障归因
当前官方支持范围是NVIDIA GPU环境。下文的GPU与NVLink健康检查依赖NVML,GPU性能计时依赖CUDA/CUPTI(NVIDIA GPU的计算与性能分析接口),通信轨迹归因也主要分析NCCL事件;心跳、日志整理和基于日志的重启建议虽然没有全部直接依赖GPU,但项目并未承诺支持AMD/ROCm等非NVIDIA平台。
硬件与存储健康检查
训练进程失败不一定是代码错误,也可能是GPU、服务器内部连接、网卡或共享存储出了问题。NVRx会读取系统已经提供的状态和错误计数,帮助判断一台服务器是否还适合参加下一轮训练。
先了解这里涉及的硬件名称:
NVML是NVIDIA提供的GPU管理接口,程序可以通过它读取GPU状态和错误信息;NVLink是GPU之间的高速连接,用于快速交换数据;NIC就是网卡,文中的InfiniBand NIC是训练集群常用的高速网络设备;Lustre是一种多台服务器共同访问的分布式文件系统,常用于存放大模型检查点。
当前版本提供以下检查:
| 检查对象 | 读取的信息 | 能发现什么 |
|---|---|---|
GPU(GPUHealthCheck) | 驱动通过NVML给出的恢复建议 | 是否需要重置GPU、重启服务器,或者先停止GPU间通信再处理 |
GPU间连接(NVLHealthCheck) | 各条NVLink是否启用 | 是否存在已经被禁用的连接;单条连接无法读取时只记录警告,继续检查其他连接 |
GPU间连接的近期错误(NVLinkWindowHealthCheck) | 一段时间内新增的恢复、重放、重试和接收错误 | 连接是否持续产生异常,而不只是过去偶然出现过一次错误 |
高速网卡(NicHealthCheck) | 操作系统记录的InfiniBand链路掉线次数 | 靠近本机GPU的高速网卡最近是否发生新的掉线 |
| 共享存储 | Lustre状态、挂载是否存在、指定路径能否读取 | 训练所需的共享存储或检查点目录是否基本可访问 |
读取GPU恢复建议的接口要求r570或更高版本的驱动,旧驱动会禁用这项检查。某些信号无法获得时,NVRx采用“记录警告但暂时放行”的做法。例如,可选的服务器健康服务不存在或返回内容无法解析时,启动器不会仅凭这一点将服务器判为故障。
因此,这些检查适合在重启前快速筛除明显异常的设备,不能替代数据中心监控、驱动日志和硬件管理系统,也不能保证通过检查的服务器一定没有问题。
分布式日志
分布式训练可能同时产生几百甚至几千份日志。只看某一个进程的输出,很难还原故障前后的完整过程。NVRx Logger会在每条日志中补充服务器、训练进程编号和源码位置,便于把不同进程在同一时间发生的事件对应起来。
设置NVRX_NODE_LOCAL_TMPDIR后,每个训练进程先把日志写到本机临时文件,再由本机编号为0的进程合并成一份服务器日志。这样可以避免所有进程频繁争用网络文件系统。
默认的“分布式日志”只负责单台服务器内的合并,并不等同于集中式日志平台。启动器还提供一种可选方式:各服务器通过gRPC网络通信把日志流发送到承担协调服务的主机,再由一个写入者保存到共享存储。日志会按每次重启分别保存,方便比较故障前后的多次尝试。
官方文档把这种集中方式定义为“尽力而为”(best-effort):进程突然崩溃时,最后几条日志和崩溃调用栈仍可能来不及发送。因此,排查严重故障时还应保留监控进程日志,以及操作系统在程序崩溃时生成的内存现场文件(Core Dump)。
故障归因
健康检查和日志收集解决了“获得证据”的问题,故障归因则尝试根据这些证据回答“可能是谁出了问题”。当前有两个主要方向:
- 分析进程间通信轨迹:
PyTorch Flight Recorder类似一个记录集合通信事件的“黑匣子”。分析器比较各训练进程已经开始和已经完成的通信操作,找出哪个进程没有进入某次通信,或者进入后一直没有完成。它只提供线索,不直接决定重启还是停止。 - 综合日志给出恢复建议:实验性的
Restart Agent读取按时间交错排列的全局训练日志,也可以参考此前几次重启记录。它先提取能够由规则确认的证据,再可选调用模型生成结构化解释,最后结合重试规则和剩余重启次数,给出“停止”(STOP)或“重启”(RESTART)建议。
为了避免分析过程延长停机时间,启动器默认不会等待归因结果:它提交本轮日志后立即开始下一轮训练,后台再获取分析结论。即使分析结果建议STOP,参数--ft-attribution-stop-action的默认值log也只会记录建议,不会真正结束作业。只有把它显式设置为no-restart,停止建议才会阻止下一次重启。
因此,NVRx不能保证自动、准确地区分GPU、NVLink、网卡和训练代码故障。更准确地说,它负责把硬件状态、各训练进程的事件、日志和通信轨迹整理成较完整的故障现场,并提供仍处于实验阶段的分析与恢复建议。最终根因仍需要结合集群监控、驱动和操作系统日志,以及训练程序自身日志来确认。
常见问题
关于对SLURM集群的支持
该项目上有提到对SLURM集群的支持,那么该项目是否只能运行在SLURM集群?
不是。NVRx整体上不绑定SLURM,它的异步检查点、本地检查点、慢rank检测和inprocess.Wrapper等核心组件主要依赖PyTorch分布式运行环境,可以独立接入其他集群。ft_launcher也基于torchrun实现,并支持大部分torchrun命令行参数。
参考资料
- NVIDIA Resiliency Extension项目
- NVRx官方文档首页
- Fault Tolerance Usage Guide源码快照
- In-process Restart Usage Guide源码快照
- Async Checkpointing Usage Guide源码快照
- Local Checkpointing Usage Guide源码快照
- Straggler Detection Usage Guide源码快照
- Failure Attribution文档源码快照
- Shared Utilities文档源码快照
- 硬件健康检查实现
- 项目依赖与打包配置
- NVRx Release Notes
- PyTorch Lightning集成废弃声明