Follow the bytes after the API returns
An asynchronous checkpoint call can return before durable writing finishes, but the state still has to live somewhere. A common path materializes a state dictionary, stages device tensors into CPU memory, transforms or serializes them, and writes buffers to local or remote storage. In PyTorch 2.6's default Distributed Checkpoint path, CPU staging is blocking and the storage save then runs in another thread, so asynchronous saving does not imply that every preparation step overlaps training. Once the call returns, training can resume while staged allocations remain live, raising peak host memory.
PyTorch Distributed Checkpoint describes asynchronous save as de-staging the state dictionary to staging storage, which defaults to CPU memory, before saving in another thread. That detail changes capacity planning. Host demand is not just the training process baseline. It includes staged tensor bytes, serialization and I/O buffers, allocator fragmentation, data-loader queues, communication libraries, page cache effects, and any overlapping checkpoint generation.
Distinguish pageable and pinned pressure
Pinned, or page-locked, host memory supports efficient asynchronous CPU-to-GPU transfers because the operating system cannot relocate or swap those pages during direct memory access. NVIDIA's CUDA best-practices documentation warns that excessive pinned allocation can reduce overall system performance and that the useful amount must be measured. Pinned bytes therefore deserve their own metric, not merely inclusion in total resident memory.
Checkpoint staging and data loading can compete for the same host budget even when they use different allocators. A loader may maintain prefetched pinned batches while the checkpointer holds device-to-host copies. Meanwhile pageable buffers and page cache can push the node toward reclaim or an out-of-memory kill. An async design that protects GPU utilization but kills the process is not resilient checkpointing.
Estimate a safe envelope before enabling overlap
Use one non-overlapping byte budget. Starting from total usable RAM, subtract steady peak training RSS, loader and service memory, and an operating-system reserve. The remainder is the checkpoint envelope. Alternatively, start from measured MemAvailable at the same steady state and subtract only the reserve and other incremental consumers, not the training RSS a second time. Estimate checkpoint working set as staged state plus transform and writer buffers, multiplied by the maximum concurrent generations. Treat page cache carefully because RSS and cgroup accounting may already include parts of it.
Consider an illustrative node with 512 GiB RAM, a measured 250 GiB training peak, 70 GiB for loaders and services, and a 64 GiB operating reserve. Headroom is 512 - 250 - 70 - 64 = 128 GiB. If one checkpoint stages 92 GiB and temporarily needs 24 GiB of serialization buffers, one generation uses about 116 GiB. Two overlapping generations need about 232 GiB and exceed the envelope. These are illustrative values; actual memory behavior varies.
| Component | Illustrative peak |
|---|---|
| Training RSS | 250 GiB |
| Loaders and services | 70 GiB |
| OS reserve | 64 GiB |
| One staged checkpoint | 116 GiB |
| Remaining margin after one | 12 GiB |
Make backpressure explicit
Allow at most one in-flight checkpoint until measurement proves a larger bound. Before starting another, either wait for the prior future to complete, skip the new request with a visible metric, or fall back to a synchronous path if the recovery point objective requires it. Never queue unbounded save requests. A queue merely converts storage slowdown into delayed memory exhaustion.
Use two admission tests: projected headroom after staging must remain above the reserve, and the oldest in-flight save must remain within its latency service level. For example, admit only when projected available memory stays above 15 percent of node RAM and in-flight count is zero. Those thresholds are policy examples, not universal constants. Also propagate asynchronous errors, because a completed future with an exception is not a durable checkpoint.
Measure the real peak across the whole node
Capture process RSS and proportional set size where available, cgroup current and peak memory, host available memory, swap activity, major page faults, pinned or locked bytes, and checkpoint queue depth. Sample fast enough to catch staging spikes. Mark state creation, device-to-host staging, serialization, write, completion, and buffer release as separate time ranges. PyTorch profiler supports labeled ranges and CPU and device activities, which helps align application phases with system telemetry.
Run a controlled sequence after warm-up: five minutes without saving, one synchronous save, one asynchronous save, then an attempted second save before completion. Repeat under normal loader prefetch and storage contention. The diagnostic rule is simple: if memory does not return near its pre-save band after future completion, investigate retained references or allocator behavior; if the second request creates a near-linear second spike, enforce single-flight backpressure.
Choose overlap only when it pays
Compare exposed pause reduction against additional memory risk. If synchronous saving adds 40 seconds but asynchronous staging still blocks for 30 seconds, the recovered overlap is only 10 seconds while buffers may live for minutes. Conversely, fast staging followed by a long remote write can produce useful overlap if single-flight buffering fits comfortably. Report training step p50 and p99, checkpoint completion latency, and peak host memory together.
A limitation is that RSS does not perfectly describe pressure: shared pages, file cache, NUMA placement, and allocator reservations complicate attribution. Pinned-memory counters can also be framework and driver specific. Validate under the same container limits and worker topology used in production. A larger-memory node may be justified by the measured envelope, but additional RAM should not substitute for bounded concurrency.
Test failure and define rollback
Include failure injection in acceptance. Slow the checkpoint destination, force one write to fail, and verify that buffers are released, the future reports the error, and the next eligible save can proceed. Then hold the destination slow for longer than one checkpoint interval and confirm that backpressure bounds memory rather than accumulating work. This test distinguishes a fast-path demonstration from a safe asynchronous design.
Set a production rollback trigger before rollout. One defensible example is to disable asynchronous saving if host available memory crosses the reserve twice in an hour, checkpoint completion p99 exceeds two configured intervals, or an in-flight generation survives after its future has completed. Thresholds must reflect local observations. The key is that the system returns to a known synchronous mode before memory pressure becomes an operating-system kill.
