<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Fault Tolerance | 木叶吟</title><link>https://yezhisheng.me/zh/tag/fault-tolerance/</link><atom:link href="https://yezhisheng.me/zh/tag/fault-tolerance/index.xml" rel="self" type="application/rss+xml"/><description>Fault Tolerance</description><generator>Wowchemy (https://wowchemy.com)</generator><language>zh-Hans</language><copyright> 又拍云提供CDN服务
京ICP备16021535号-1</copyright><lastBuildDate>Sun, 17 May 2026 15:00:00 +0800</lastBuildDate><image><url>https://yezhisheng.me/media/icon_hu585778a5d9441f07b7d64e1beae1be58_320895_512x512_fill_lanczos_center_3.png</url><title>Fault Tolerance</title><link>https://yezhisheng.me/zh/tag/fault-tolerance/</link></image><item><title>GPU 任务的暂停、恢复与迁移：调度器一直缺的那块拼图</title><link>https://yezhisheng.me/zh/post/gpu-pause-resume-migration/</link><pubDate>Sun, 17 May 2026 15:00:00 +0800</pubDate><guid>https://yezhisheng.me/zh/post/gpu-pause-resume-migration/</guid><description>&lt;p>如果正在运行的 GPU 任务能像普通 CPU 进程一样行动，GPU 集群调度会容易很多。暂停它，移动它，在别处恢复它。当更高优先级任务到来时回收设备。通过迁移修复碎片化，而不杀掉用户工作。&lt;/p>
&lt;p>现实中，GPU 调度恰恰卡在这里。&lt;/p>
&lt;p>CPU 进程可以通过保存 address space、file descriptor 和 kernel-visible state 来 checkpoint。GPU 任务则有另一半生命在普通进程抽象之外：CUDA context、device allocation、stream、event、library handle、正在运行的 kernel，以及驻留在 GPU memory 中的数据。操作系统并不天然知道如何 serialize 这些状态。调度器可以停止 host process，但这不等于拥有一个正确、可迁移的 GPU computation checkpoint。&lt;/p>
&lt;p>&lt;a href="https://yezhisheng.me/publication/flowgpu/">FlowGPU&lt;/a> 的目标就是把 GPU checkpoint/restore 变成系统原语。在 FlowGPU 成为完整系统之前，我写过 &lt;a href="https://github.com/yzs981130/cudaw" target="_blank" rel="noopener">cudaw&lt;/a> 作为代码库的最早版本：一个 CUDA wrapper prototype，用来 interpose runtime call、跟踪 GPU object、翻译 application-visible address，并让 pause/resume/migration 在不修改 CUDA application 的情况下变得可能。&lt;/p>
&lt;h2 id="为什么调度器需要这个原语">为什么调度器需要这个原语&lt;/h2>
&lt;p>Pause/resume 和 migration 会改变调度器能做什么。&lt;/p>
&lt;p>没有 GPU checkpoint/restore 时，抢占往往很粗暴。调度器可以杀掉任务，要求 framework 在预定义 training boundary checkpoint，或者等用户代码主动配合。这对某些 training loop 可以接受，但和集群事件并不匹配。高优先级任务可能现在就到达，GPU 可能现在就故障，碎片化 placement 也可能现在就需要修复。Framework-level checkpoint 通常服务于应用便利性，而不是调度器控制。&lt;/p>
&lt;p>有了透明 GPU checkpoint，scheduler 就拥有了更强的操作：&lt;/p>
&lt;ul>
&lt;li>暂停任务并释放 GPU memory；&lt;/li>
&lt;li>稍后在同一张 GPU 上恢复；&lt;/li>
&lt;li>迁移到另一张 GPU 或另一个节点；&lt;/li>
&lt;li>为 fault tolerance 做周期性 checkpoint；&lt;/li>
&lt;li>通过迁移任务修复集群碎片化；&lt;/li>
&lt;li>用更少用户代码介入支持 elastic scaling 和 priority scheduling。&lt;/li>
&lt;/ul>
&lt;p>这是调度策略和 GPU execution 之间缺失的连接。调度器也许知道正确决策是什么，但如果没有安全 migration primitive，它就无法低成本执行这个决策。&lt;/p>
&lt;h2 id="cuda-wrapper-视角">CUDA Wrapper 视角&lt;/h2>
&lt;p>我的 &lt;code>cudaw&lt;/code> prototype 的基本想法，是在应用和 CUDA runtime 之间放一个 wrapper。应用不再直接和 &lt;code>libcudart&lt;/code> 交互，而是由 wrapper 拦截 allocation、memory copy、kernel launch 等 CUDA call。从调度器的角度看，这会形成一份 execution log，以及 GPU state 的 shadow view。&lt;/p>
&lt;p>这个 wrapper layer 可以记录哪些 device memory region 存在、哪些 host-side pointer 与之对应、数据如何在 CPU 和 GPU 之间移动、哪些 kernel 带着哪些参数被 launch。它也可以维护 virtual GPU address：应用看到稳定的 logical address，而 wrapper 在底层把它们映射到真实 CUDA allocation。这层 indirection 让 restore 和 migration 变得可能，因为恢复后的任务在目标设备上可能拿到不同的 physical GPU address。&lt;/p>
&lt;p>在一个简化的 checkpoint flow 中，wrapper 到达 safe point，同步 GPU work，把 live GPU memory 拷贝到 checkpoint image，保存足以重建 CUDA state 的 metadata，然后释放设备。Restore 则反向执行：在目标 GPU 上分配 memory，重建 mapping，把数据拷贝回来，replay 必要的 CUDA setup call，并继续执行。&lt;/p>
&lt;p>这个早期 prototype 捕捉到的核心直觉，后来也影响了 FlowGPU。GPU migration 不是魔法，而是状态重建。难点在于，让重建后的世界和原来的世界无法区分。&lt;/p>
&lt;h2 id="纯-wrapper-设计难在哪里">纯 Wrapper 设计难在哪里&lt;/h2>
&lt;p>Wrapper 思路很有力量，但边界情况非常残酷。&lt;/p>
&lt;p>第一，CUDA state 远不止 &lt;code>cudaMalloc&lt;/code> 和 &lt;code>cudaMemcpy&lt;/code>。真实应用会使用 stream、event、cuBLAS、cuDNN、NCCL、memory pool、unified memory、graph execution 和 framework allocator。许多对象是 opaque 的：CUDA 暴露的是 handle，而不是可序列化的内部状态。Checkpoint system 必须记录并 replay 创建或修改它们的操作。&lt;/p>
&lt;p>第二，address identity 很重要。Pointer value 可能存储在应用数据结构、kernel argument、framework metadata 或 library state 里。如果 restore 后程序看到不同的 GPU virtual address，即使 bytes 被正确拷贝，应用也可能出现非常隐蔽的错误。&lt;/p>
&lt;p>第三，深度学习框架会隐藏 memory behavior。PyTorch 和 TensorFlow 通常会预留大块 GPU memory 并复用它们。在某个时刻，其中很多 reserved memory 可能并不活跃。Naive checkpoint 如果保存 runtime 分配过的一切，就会产生巨大的 checkpoint image，即使真正有用的 live state 小得多。&lt;/p>
&lt;p>第四，distributed training 是一个同步问题。多 GPU 任务的一致 checkpoint 需要安全暂停所有参与 rank。遇到 NCCL communication 时，如果暂停 blocking send/receive 的一侧，而另一侧还在等待，checkpoint protocol 本身就可能 deadlock。&lt;/p>
&lt;p>这些正是 FlowGPU 试图系统化解决的问题。&lt;/p>
&lt;h2 id="flowgpu-的核心动作">FlowGPU 的核心动作&lt;/h2>
&lt;p>FlowGPU 的关键 insight 是，以前的 system-level GPU checkpoint/restore 设计经常把 C/R 和 API forwarding 绑在一起。在 API forwarding 中，所有 GPU operation 都经过一个 privileged central process。这让 interception 和 state separation 更容易，但会引入 runtime overhead，在 sharing 场景下产生 GPU address conflict，并阻碍部分 GPU feature。&lt;/p>
&lt;p>FlowGPU 把 checkpoint/restore 从 virtualization 中解耦出来。&lt;/p>
&lt;p>正常执行时，每个任务使用 per-task intercept library。GPU operation 仍然属于该任务，并直接访问 GPU，避免 central forwarding process 带来的 IPC overhead。需要 checkpoint 时，FlowGPU 创建一个 ghost process。Ghost process 临时接管 GPU state，而原 process 变成一个传统 CPU process，可以用 CRIU checkpoint。GPU state 和 CPU state 并行保存，再在 restore 时重新组合。&lt;/p>
&lt;p>这个设计保留了 interception 有用的部分，同时避免正常执行期间所有 GPU operation 都绕过 virtualization server。&lt;/p>
&lt;h2 id="让-checkpoint-更小也更可靠">让 Checkpoint 更小也更可靠&lt;/h2>
&lt;p>FlowGPU 增加了几项对深度学习任务尤其重要的机制。&lt;/p>
&lt;p>Active memory identification 避免保存整个 framework-reserved memory pool。FlowGPU 在稳定的 DL framework backend allocation/free interface 上插入 memory stub，跟踪真正活跃的 memory region。它也可以在 checkpoint 前短暂等待 active memory 降到 training iteration 中较低的位置。原因是 training 中 active memory 可能在 iteration 末尾和 forward/backward 中 activation 最重的阶段之间剧烈波动。&lt;/p>
&lt;p>Virtual memory management 用于保留 GPU address identity。FlowGPU 拦截 GPU allocation，并使用 &lt;code>cuMemAddressReserve&lt;/code>、&lt;code>cuMemCreate&lt;/code>、&lt;code>cuMemMap&lt;/code> 等 CUDA VMM API，在 restore 时保留并 remap 相同的 virtual address。这移除了 pointer-rich GPU application 中一类主要 correctness bug。&lt;/p>
&lt;p>Record/replay 用来处理 opaque runtime object。CUDA stream、event、context 和 library handle 无法简单读成 bytes，因此 FlowGPU 记录创建或修改它们的操作，并在恢复期间 replay。&lt;/p>
&lt;p>Pause mechanism 也针对 distributed task 做了细化。FlowGPU 会协调多个 rank 的暂停，但为了避免一种已知 NCCL deadlock pattern，如果完整 pause 无法达成，它会在 timeout 后恢复所有 instance。这个细节很小，后果很大：checkpointing 不能引入一个比原问题更糟的 failure mode。&lt;/p>
&lt;p>对于 multi-GPU task，FlowGPU 还做了细粒度 deduplication。Replicated model parameter 可能出现在多张 GPU 上，但 runtime memory block 很少完全一致。FlowGPU 对固定大小 region 做去重，降低分布式任务的 checkpoint image size。&lt;/p>
&lt;h2 id="这对调度意味着什么">这对调度意味着什么&lt;/h2>
&lt;p>一旦 GPU pause/resume 变得实用，很多调度策略就更现实了。&lt;/p>
&lt;p>Priority scheduling 可以抢占低优先级 GPU 任务，而不丢掉它的全部进度。Fairness scheduling 可以用更低扰动在时间上重新分配 service。Fragmentation-aware scheduler 可以迁移任务，重建 gang-scheduled workload 需要的连续 placement。Fault-tolerance system 可以按调度器控制的间隔 checkpoint，而不只依赖 framework checkpoint。Elastic scheduler 可以更清晰地 shrink、expand 或 relocate 任务。&lt;/p>
&lt;p>这个原语也改变了 GPU sharing 的经济性。如果一个任务可以快速 pause 和 restore，集群就能在 bursty demand 下采取更激进的动作。Online inference、training 和 HPO workload 不必完全生活在彼此隔离的资源孤岛里；当优先级变化时，调度器有了更好的移动工作方式。&lt;/p>
&lt;p>FlowGPU 的评估展示了细节为什么重要。论文报告称，因为任务可以不经过 API forwarding 直接访问 GPU，它在正常 single-GPU execution 中没有 runtime overhead。对于 DL task，相比 POS，FlowGPU 将 checkpoint pause time 降低 6.2x 到 15x；相比 Singularity，最多降低 10.4x。Restore time 相比 POS 降低 12x 到 18x，相比 Singularity 最多降低 4.1x。Migration 方面，FlowGPU 最多比 Singularity 快 2.1x，比 PyTorch framework-level checkpointing 快 1.7x 到 4.5x。&lt;/p>
&lt;p>这些数字不只是 checkpointing 结果，也是调度能力被释放出来的结果。慢 checkpoint 是调度器不敢频繁使用的 policy；快而透明的 checkpoint 才会变成真正的 control knob。&lt;/p>
&lt;h2 id="小结">小结&lt;/h2>
&lt;p>GPU 调度经常被讨论成算法问题：fairness metric、placement heuristic、bin packing、elastic allocation 和 priority queue。但调度器的能力上限取决于下面的 execution primitive。&lt;/p>
&lt;p>&lt;code>cudaw&lt;/code> 是我对 wrapper-level 直觉的第一版实现：interpose CUDA，virtualize application 看到的东西，并在需要时重建 GPU state。FlowGPU 把这个直觉推进成更完整的系统设计：per-task interception 保证低开销，ghost process 实现 state separation，active-memory tracking 缩小 image，VMM 保证 address correctness，distributed pause logic 支撑 multi-GPU workload。&lt;/p>
&lt;p>最终结果是 policy 和 mechanism 之间更清晰的边界。调度器决定一个任务什么时候应该 pause、resume 或 move；checkpoint/restore layer 让这个决策足够安全，可以真正执行。&lt;/p>
&lt;p>Paper: &lt;a href="https://yezhisheng.me/publication/flowgpu/">FlowGPU: Transparent and Efficient GPU Checkpointing and Restore&lt;/a>&lt;br>
Early codebase: &lt;a href="https://github.com/yzs981130/cudaw" target="_blank" rel="noopener">yzs981130/cudaw&lt;/a>&lt;/p></description></item><item><title>ResiHP：大模型训练故障下的动态混合并行</title><link>https://yezhisheng.me/zh/post/resihp/</link><pubDate>Sun, 17 May 2026 14:00:00 +0800</pubDate><guid>https://yezhisheng.me/zh/post/resihp/</guid><description>&lt;p>Reference reading: &lt;a href="https://zhuanlan.zhihu.com/p/2036192731547035544" target="_blank" rel="noopener">大模型训练遇到 GPU 故障怎么办？我们的做法是动态调整 3D 并行&lt;/a>.&lt;/p>
&lt;p>大规模 LLM 训练不是一个单独的分布式系统问题，而是好几个问题叠在一起。&lt;/p>
&lt;p>当训练规模达到数百甚至数千张 GPU 时，故障不再是罕见事件。有些设备会直接消失；另一些设备还活着，但变慢了。后者尤其麻烦：fail-slow GPU 不会让训练任务崩溃，却会拖慢整个同步训练迭代。在 hybrid parallel training 中，这个延迟会沿着 tensor parallelism、pipeline parallelism 和 data parallelism 传播，最后让一张变慢的设备悄悄决定整个任务的速度。&lt;/p>
&lt;p>&lt;a href="https://yezhisheng.me/publication/resihp/">ResiHP&lt;/a> 就是为这个场景设计的。它的核心思想是让 hybrid parallelism 变成动态结构。ResiHP 不把 3D parallel layout 当作启动后固定不变的配置，而是在检测到异常设备后，围绕剩余资源重新组织训练计划。&lt;/p>
&lt;h2 id="为什么故障检测很难">为什么故障检测很难&lt;/h2>
&lt;p>最直观的信号是 iteration time。如果某一次 iteration 变慢了，也许就有设备故障。&lt;/p>
&lt;p>这个逻辑对 LLM 训练来说太脆弱。&lt;/p>
&lt;p>现代 LLM workload 经常使用 variable-length sequences。即使用 sequence packing 控制 token budget，真实 attention cost 仍然取决于每个 micro-batch 内部的 sequence length。一个包含许多长序列的 packed batch，自然会比短序列更多的 packed batch 更慢。Pipeline scheduling 又增加了一层噪声：观测到的 iteration time 不只是某个 micro-batch cost，而是多个 pipeline stage 上 forward、backward 和 weight-update chunk 共同形成的 critical path。&lt;/p>
&lt;p>这也是知乎文章强调的点：detector 不能盯着 raw iteration time，把每个 spike 都判成故障。它首先要估计，如果所有设备都健康，这个 iteration 本来应该花多少时间。&lt;/p>
&lt;h2 id="用-flops-归一化检测信号">用 FLOPs 归一化检测信号&lt;/h2>
&lt;p>ResiHP 的 Detector 会用期望计算量对 iteration time 做归一化。&lt;/p>
&lt;p>在 micro-batch 层面，它根据 packed sequence structure 估计工作量。Attention cost 并不是 sequence length 的线性函数，所以模型不仅数 token，还会考虑 quadratic attention cost。在 pipeline 层面，ResiHP 模拟 forward、backward 和 weight-update chunk 的 schedule，预测健康 iteration 的 critical path。&lt;/p>
&lt;p>只有在完成这种归一化之后，ResiHP 才比较 observed time 和 expected time。如果二者之间的 gap 仍然异常，系统才把它当作 fail-slow signal，而不是普通的 sequence-length variation。Fail-stop 则通过 missing heartbeat 另行处理。&lt;/p>
&lt;p>这个区分很重要，因为 false positive 代价很高。如果一个 resilient training system 经常把正常工作负载偏斜误判成硬件故障，它就会毫无必要地反复重组训练任务。ResiHP 试图让检测足够轻量，能够在线使用；同时也足够准确，让 adaptation 只在真正出问题时发生。&lt;/p>
&lt;h2 id="为什么混合并行让恢复更棘手">为什么混合并行让恢复更棘手&lt;/h2>
&lt;p>一旦某个设备被识别为不健康，最简单的反应是把它移除。&lt;/p>
&lt;p>但这通常不够。&lt;/p>
&lt;p>在纯 data parallelism 中，少一个 worker 主要意味着 replica 数量下降。在 hybrid parallelism 中，一个设备参与的是结构。它可能同时是 tensor-parallel group 里的一个 rank、pipeline 的一个 stage，以及 data-parallel replica 的成员。如果一个 tensor-parallel rank 故障，整个 TP group 都受影响。如果一个 pipeline stage 变慢，上游和下游 stage 都会等待。如果一个 data-parallel replica 落后，同步也会被拖慢。&lt;/p>
&lt;p>故障是局部的，但性能损伤是全局的。&lt;/p>
&lt;p>因此 ResiHP 不采用单一 workaround，而是在多个层面适配。它会改变 parallelism group size，重新划分 pipeline stage 上的 model layer，调整工作调度，并在 replica 之间重新分配任务。&lt;/p>
&lt;h2 id="动态重组-3d-并行">动态重组 3D 并行&lt;/h2>
&lt;p>ResiHP 的 Scheduler 负责把检测结果转化为新的训练计划。&lt;/p>
&lt;p>对于 tensor parallelism，ResiHP 可以围绕健康设备收缩或重组 TP group。目标不是简单丢弃受影响 group 里的所有设备，因为那可能浪费太多健康 GPU。调度器会搜索更合适的 group size 和 membership，在避开慢 rank 或故障 rank 的同时保留尽可能多的有效计算。&lt;/p>
&lt;p>对于 pipeline parallelism，ResiHP 可以重新平衡 model partitioning。慢 stage 不应该继续承担和健康 stage 一样多的 layer。如果某个 stage 变慢，调度器可以给它分配更少 layer，并把工作转移给更健康的 stage，从而降低 pipeline bottleneck。&lt;/p>
&lt;p>对于 data parallelism，ResiHP 使用 workload migration。如果某个 replica 落后，而另一个 replica 还有余量，调度器可以迁移一部分工作，让整体进度更平衡。这一点尤其有用，因为 data-parallel replica 在逻辑上对称，但在设备故障或性能退化之后，实际速度可能分化。&lt;/p>
&lt;p>关键工程点在于，这些 adaptation 是协调发生的。只调 TP 可能制造 pipeline imbalance；只调 PP 可能让健康 GPU 利用不足；只调 DP 可能没有消除原始瓶颈。ResiHP 把 layout 当作一个相互连接的 3D object 来处理。&lt;/p>
&lt;h2 id="executor-如何兜住重配置">Executor 如何兜住重配置&lt;/h2>
&lt;p>新的 plan 只有在 runtime 能执行时才有意义，而且 recovery 本身不能变成第二次故障。&lt;/p>
&lt;p>ResiHP 的 Executor 负责动态重配置的具体机制。它在新的 parallel layout 下重建 model 和 optimizer state，更新通信策略，并支持针对新 group 的高效 data movement。也正是在这里，系统从调度策略进入真正的 fault-tolerant training。&lt;/p>
&lt;p>Executor 对 fail-stop recovery 也很重要。如果 GPU 消失了，系统必须在重新分布受影响 model shard 和工作负载的同时保持训练连续性。如果 GPU 只是变慢了，系统又必须避免过度反应，同时降低它对全局 critical path 的影响。&lt;/p>
&lt;h2 id="resihp-带来了什么">ResiHP 带来了什么&lt;/h2>
&lt;p>ResiHP 在 256-GPU 集群上、多个故障场景中进行了评估。论文报告称，它达到接近最优的故障检测准确率，并相比已有 resilient training system 将训练吞吐提升 1.13x 到 2.22x。&lt;/p>
&lt;p>更大的经验是：LLM 训练的 resilience 不能只是一个 checkpoint-and-restart loop。Hybrid parallelism 本来就是大规模训练能够成立的结构，因此 resilience 也必须理解这个结构。ResiHP 把问题拆成三个层次：&lt;/p>
&lt;ul>
&lt;li>这次 slowdown 是真实故障，还是 sequence-length variation？&lt;/li>
&lt;li>3D parallel layout 中真正受损的是哪一部分？&lt;/li>
&lt;li>TP、PP 和 DP 应该如何一起改变，才能让训练任务持续前进？&lt;/li>
&lt;/ul>
&lt;p>我喜欢 ResiHP 的地方就在这里：它把故障处理看成一个 dynamic parallelism 问题，而不只是 device replacement 问题。&lt;/p>
&lt;p>Paper: &lt;a href="https://yezhisheng.me/publication/resihp/">ResiHP: Taming LLM Training Failures with Dynamic Hybrid Parallelism&lt;/a>&lt;br>
Preprint: &lt;a href="https://arxiv.org/abs/2605.06374" target="_blank" rel="noopener">arXiv:2605.06374&lt;/a>&lt;/p></description></item></channel></rss>