Young-Daly Checkpoint Interval

The Young–Daly checkpoint interval is the rule of thumb for how often a long-running job should save its state when failures arrive at random. Saving too often wastes time on writes, and saving too rarely loses a lot of work at each failure. Young (1974) gave a first-order approximation for the interval that minimizes the total waste, and Daly (2006) analyzed it with higher-order terms and stated when it holds.

The formula

Let \( \delta \) be the time the job is blocked while writing one checkpoint, and \( M \) the mean time between failures (MTBF) of the job as a whole. Young’s approximation for the compute time between checkpoints is

\[ T_{\text{opt}} \approx \sqrt{2\,\delta\,M} \]

The reasoning is a balance of two terms. Checkpointing costs \( \delta / T \) per unit of useful work. Each failure, which happens about once per \( M \), loses on average half an interval of work, so failures cost \( T/(2M) \). The total \( \delta/T + T/(2M) \) falls and then rises as \( T \) grows, and setting its derivative \( -\delta/T^2 + 1/(2M) \) to zero gives the square root. Because the minimum is shallow, an interval within a factor of about two of the optimum costs only a little more, which is why an estimate is good enough in practice. The optimum grows only with the square root of each parameter: a checkpoint four times as expensive, or a cluster four times as reliable, calls for an interval twice as long.

Restart time \( R \) adds \( R/M \) to the waste, but it does not depend on \( T \), so it does not move the optimum in this first-order model.

Assumptions

  • Failures arrive at random at a constant rate, so the expected position of a failure in an interval is its midpoint.
  • The checkpoint time \( \delta \) is small compared with \( M \), and the job is restored from the most recent checkpoint only.
  • \( \delta \) is the time the job is blocked. If the job copies its state to memory and writes it in the background, use the blocking part.
  • \( M \) belongs to the whole job: the failure rate of one GPU multiplied across all the GPUs and hosts the job needs.

A numerical check

The script below uses an MTBF of 3.1 hours and a restart time of 10 minutes, values chosen to be a plausible magnitude. For each checkpoint cost it finds the interval that minimizes the expected overhead under an exponential failure model in which failures can also strike during a checkpoint write and during a restart, then compares it with Young’s formula and with Daly’s refinement, \( \sqrt{2\delta M} – \delta \), which Daly’s paper recommends when \( \delta < M/2 \) (and \( M \) otherwise). Overhead is extra wall-clock time per unit of useful work. It also checks the model against a Monte Carlo simulation of 300 runs per row.

import math, random
M, R = 3.1*3600, 600.0                      # MTBF and restart (seconds)

def waste_fo(T, d):  return d/T + (T/2 + R)/M          # first-order model
def young(d, M=M):   return math.sqrt(2*d*M)
def daly(d, M=M):    return math.sqrt(2*d*M) - d if d < M/2 else M

def overhead_exact(T, d):                    # exponential failures, failures can hit save/restart
    seg = T + d
    return M*math.exp(R/M)*(math.exp(seg/M)-1)/T - 1   # extra wall time per unit of useful work

def sim(T, d, work=2e5, runs=300, seed=1):
    rnd = random.Random(seed); tot = 0.0
    for _ in range(runs):
        done = 0.0; t = 0.0
        while done < work:
            chunk = min(T, work-done); seg = chunk + d
            while True:                      # attempt one segment
                x = rnd.expovariate(1/M)
                if x >= seg: t += seg; done += chunk; break
                t += x
                while True:                  # restart, which can also fail
                    y = rnd.expovariate(1/M)
                    if y >= R: t += R; break
                    t += y
        tot += t/work - 1
    return tot/runs

for d in (20, 120, 600):
    Ty, Td = young(d), daly(d)
    grid = [Ty*k/400 for k in range(40, 1600)]
    best = min(grid, key=lambda T: overhead_exact(T, d))
    print(f"d={d:4d}s  Young {Ty/60:5.1f} min  Daly {Td/60:5.1f} min  numeric optimum {best/60:5.1f} min | "
          f"overhead at Young {overhead_exact(Ty,d):.3f}, at optimum {overhead_exact(best,d):.3f}")
d = 120; Ty = young(d)
print("check by simulation, d=120s:")
for k in (0.25, 0.5, 1, 2, 4):
    T = Ty*k
    print(f"  T={T/60:6.1f} min  first-order {waste_fo(T,d):.3f}  exact-model {overhead_exact(T,d):.3f}  simulated {sim(T,d):.3f}")
# when d is not small relative to M
for d in (0.3*M, 0.5*M):
    Ty = young(d); grid=[M*k/200 for k in range(1,600)]
    best=min(grid,key=lambda T: overhead_exact(T,d))
    print(f"d={d/3600:.2f}h  Young {Ty/3600:.2f}h numeric {best/3600:.2f}h overhead@Young {overhead_exact(Ty,d):.2f} @opt {overhead_exact(best,d):.2f}")

Output from Python 3.12.14 on macOS 26.7.1:

d=  20s  Young  11.1 min  Daly  10.8 min  numeric optimum  10.9 min | overhead at Young 0.121, at optimum 0.121
d= 120s  Young  27.3 min  Daly  25.3 min  numeric optimum  26.0 min | overhead at Young 0.227, at optimum 0.226
d= 600s  Young  61.0 min  Daly  51.0 min  numeric optimum  54.6 min | overhead at Young 0.496, at optimum 0.493
check by simulation, d=120s:
  T=   6.8 min  first-order 0.365  exact-model 0.398  simulated 0.396
  T=  13.6 min  first-order 0.237  exact-model 0.262  simulated 0.262
  T=  27.3 min  first-order 0.200  exact-model 0.227  simulated 0.225
  T=  54.6 min  first-order 0.237  exact-model 0.278  simulated 0.278
  T= 109.1 min  first-order 0.365  exact-model 0.470  simulated 0.478
d=0.93h  Young 2.40h numeric 1.83h overhead@Young 1.63 @opt 1.57
d=1.55h  Young 3.10h numeric 2.17h overhead@Young 2.67 @opt 2.50

For checkpoint costs of 20 seconds and 2 minutes, Young’s interval (11.1 and 27.3 minutes) is within about 5 percent of the numerical optimum (10.9 and 26.0 minutes), and the overhead at Young’s interval is within a thousandth of the best achievable. Daly’s subtraction of \( \delta \) lands closer in all three rows (10.8, 25.3, and 51.0 minutes against 10.9, 26.0, and 54.6). At a 10-minute checkpoint, Young’s 61.0 minutes is 12 percent above the optimum, yet the overhead differs by only 0.003 (0.496 against 0.493), another sign of how flat the minimum is. The simulation agrees with the exponential model within about two percent. Both are higher than the first-order waste formula, which ignores failures during writes and restarts (0.227 against 0.200 at 27.3 minutes). The last two rows set the checkpoint cost to 0.3 and 0.5 times \( M \), where Young’s interval is clearly too long (2.40 against 1.83 hours, and 3.10 against 2.17) and the overhead is already above 100 percent, which says the checkpoints, not the interval, are the problem.

Using it

  • Reducing \( \delta \) usually matters more than tuning \( T \), because overhead scales with \( \sqrt{\delta/M} \). Asynchronous and sharded checkpoints lower the blocking time.
  • Recompute the interval from observed failure counts. \( M \) changes with job size, hardware generation, and time.
  • \( \delta \) is not constant when many jobs write to shared storage at once, so concurrent checkpointing can push the real cost above the measured one.
  • Failures that are not independent and identically distributed, such as correlated rack failures or infant mortality, break the constant-rate assumption. The formula then gives a starting point, not an optimum.
  • The same rule applies to any restartable computation with a cost to save state, not only to training.

References


Discover more from Insightful Data Lab

Subscribe to get the latest posts sent to your email.

Similar Posts

Questions, corrections, or additional insights?

This site uses Akismet to reduce spam. Learn how your comment data is processed.