Skip to content
This repository was archived by the owner on Jul 17, 2026. It is now read-only.

feat(train): donate state into the faith-warmup and pretrain steps#971

Open
claude-spd1 wants to merge 1 commit into
feature/jaxfrom
bridge/task-donate-warmup-pretrain-steps
Open

feat(train): donate state into the faith-warmup and pretrain steps#971
claude-spd1 wants to merge 1 commit into
feature/jaxfrom
bridge/task-donate-warmup-pretrain-steps

Conversation

@claude-spd1

Copy link
Copy Markdown
Collaborator

Description

Two jitted steps rebound multi-GiB state every iteration with no buffer donation, keeping old+new copies co-resident:

  • faith-warmup step (param_decomp/train.py::make_faith_warmup_step) — components + warmup Adam state, right before the run's peak-memory phase. Now donate="all-except-first", exactly the train step's convention (the model stays a non-donated traced arg).
  • pretrain step (pretrain/train.py::make_train_step) — full model + Adam state. Now donate="all" (the loop rebinds state; tokens is fresh per step).

Because dispatch-time donation failure is silent (the lowering-time warning is the only one that exists, and equinox suppresses even that under donate="all"), the change ships with loud verification:

Stale-reference audit (the M2-class hazard): every consumer of the pre-step bindings at both call sites rebinds before the next donating call — run.py's warmup loop only dataclasses.replaces the old state; the pretrain loop's eval/save/cache-write all read the post-step state synchronously. Test loops all rebind.

No numerics change; SPEC untouched (donation is a memory-aliasing property only).

Related Issue

Bridge task donate-warmup-pretrain-steps (promoted from the 2026-07-10 JAX compile audit, donation-gaps finding). Guard precedent: #932.

Motivation and Context

Peak-memory headroom is an active concern (lp-* campaign). Without donation the warmup phase pays a full extra copy of components + Adam state exactly where headroom matters, and the pretrain trainer doubles its resident state for its whole run.

How Has This Been Tested?

  • Pointer-aliasing tests (the fix(checkpoint): make orbax-restored TrainState donatable — solve the resume OOM (S22) #932 verification pattern): test_llama_simple_mlp.py::test_faith_warmup_step_donates, test_pretrain.py::test_pretrain_step_donates_state, test_pretrain.py::test_restored_pretrain_state_is_donatable — ≥95% bytes-weighted unsafe_buffer_pointer reuse through the donating step, model arg survives.

  • Full param_decomp/tests/ green at default device count AND XLA_FLAGS="--xla_force_host_platform_device_count=4"; make test, make type, make check all clean.

  • 1-GPU H100 memory probe (donation on vs off, separate processes, 6 rebinding iterations each; compiled.memory_analysis() + device.memory_stats()):

    step donated state alias off→on peak off peak on steady in_use off→on
    faith-warmup 1.875 GiB 0 → 1.875 GiB 5.846 GiB 2.299 GiB 3.94 → 2.06 GiB
    pretrain 1.406 GiB 0 → 1.406 GiB 6.231 GiB 4.029 GiB 2.82 → 1.41 GiB

    Steady residency drops by exactly one copy of the donated state in both; alias == donated state proves aliasing is baked into the executable, the peak/in_use drop proves the runtime honored it (and no DONATION FAILED line fired).

Does this PR introduce a breaking change?

No. make_faith_warmup_step callers may no longer reuse the passed-in components/opt state after a call (all in-tree callers rebind). The pretrain _restore_latest now returns jit-output arrays (constructively fresh), transient 2×state at restore — the same cost restore already paid.

🤖 Generated with Claude Code

Crew-Address: task/donate-warmup-pretrain-steps

The faith-warmup step and the pretrain train step both rebind multi-GiB
state (components/model + Adam moments) every iteration with no buffer
donation, so old+new copies stayed co-resident — the warmup right before
the run's peak-memory phase. Donate matching the train-step convention
(S22-adjacent; no numerics change): warmup keeps the model arg
(all-except-first), pretrain donates all.

Donation failure at dispatch is silent (equinox suppresses even the
lowering-time warning), so: pretrain's restore re-materialises the orbax
tree as jit outputs (jax#18617, `checkpoint.restore_step`'s mechanism),
and a new shared `param_decomp/donation.py` provides bytes-weighted
buffer-pointer-reuse checks, wired one-shot into the first warmup
iteration and the first resumed pretrain step (prints DONATION FAILED).
Pointer-aliasing tests pin donation for both steps and for the restored
pretrain state.

Bridge task donate-warmup-pretrain-steps (from the 2026-07-10 compile
audit); guard precedent PR #932.

Crew-Address: task/donate-warmup-pretrain-steps
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant