Repository navigation
perf(optim): opt-in FP32 GPU gradient accumulation for full offload - #4221
Open
faresobeid wants to merge 17 commits into
Open
faresobeid wants to merge 17 commits into
faresobeid wants to merge 17 commits into
Conversation
…of the chunked-step gate
faresobeid
marked this pull request as ready for review
October 24, 2026 18:32
# Conflicts: # tests/unit/train/test_state_offload.py
The chunked CPUOffloadOptimizer step returns before its chunks run. They update on a side stream while the next forward runs, enqueued as the forward reaches them (8 up front, then 4 ahead of the forward), so the CPU never fills the CUDA launch queue. Each FSDP parameter group waits for the chunk holding its parameters before its all-gather. The step's hyperparameters and gradients are captured at step time, so the math is unchanged. model.offload_overlap_forward (default true) turns it on for AdamW. Anything else that reads the parameters calls wait_for_optimizer_step first: weight broadcasts in the SFT and RL loops; checkpoint saves already wait through prepare_checkpoint_save. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
model.offload_overlap_forward now defaults to false. The overlapped step and the pre-broadcast wait have not been exercised in an RL run yet, nor with sparse weight deltas; enable it explicitly until they have. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # src/prime_rl/trainer/sft/train.py
…-gpu-grad-accum # Conflicts: # packages/prime-rl-configs/src/prime_rl/configs/trainer.py
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands after #4427: this branch merges it (both add a
model.offload_*field next to each other) and current main.With
model.offload = "full", every backward hands each parameter's BF16 gradient toGradientOffloadManager, which copies it to the CPU through 4 pinned slots and adds it into an FP32 accumulator on a CPU thread. With N micro steps per step that is N full gradient copies over PCIe and N-1 CPU adds. The copies share host bandwidth with activation offloading and weight copies; when they fall behind, gradients wait on the GPU for a free slot.model.offload_grad_accumulation = "gpu"(opt-in, default"cpu") sums the gradients of the non-final micro steps in FP32 on the GPU, then sends the sum through the same transfer path once per step.param.gradis released as before.finish_backward. The CPU accumulator ends up with the same FP32 sum, in the same order, as the CPU path.Validation (branch merged with main and #4427, f829bc9)
Numerics
cpuaccumulation.offload = "full", AdamW, 4 micro steps per rank, 10 steps, run through the ported SFT loop (trainer/train.py). The loss is identical at every step:cpu6.229171 → 5.484061 in both of its runs,gpu6.229171 → 5.484061. Full offload turns off gradient clipping, so there is no grad norm to compare. Steady step time 1.35 / 1.30 s → 1.20 s; peak memory 4.79 → 5.07 GiB (+4 bytes per local parameter).Performance
Measured on the merged branch (one 8×H200 node, full offload, steady steps 2–5):
offload_grad_accumulation)Per-step loss and mismatch KL are identical between CPU and GPU accumulation in every arm.
The 2-GPU bitwise check passes: master weights and parameters are bitwise equal on both ranks.
GPU accumulation pins one FP32 accumulator per parameter, so peak memory is flat at 98.1 GiB. CPU accumulation's peak grows with micro steps and is the larger of the two at 8.
The gain shows at 8 micro steps (−8.5% step time and less peak memory) and is small at 4.
tests/unit/train,tests/unit/utils,tests/unit/test_configs.py(-m "not gpu"): all pass excepttest_cpu_adam, which also fails on main in this environment (noninjaon PATH). ruff is clean.Note
High Risk
Changes gradient pipelines, FSDP unshard synchronization, and optimizer stepping timing—areas that affect training correctness and can race with weight broadcast if waits are wrong.
Overview
Adds two optimizer offload tuning knobs on
ModelConfig:offload_grad_accumulation("cpu"default,"gpu"only with **offload = "full") andoffload_overlap_forward(default false, AdamW +offload = "optimizer").For full offload,
GradientOffloadManagercan sum micro-batch gradients in FP32 on GPU and copy once per step, trading a little GPU memory for less host PCIe traffic. For state-only offload,CPUOffloadOptimizeruses a new_ChunkedSteppath that streams AdamW moment states through staged GPU buffers instead of moving all state for each step; withoverlap_model, chunks update on a side stream while the next forward runs, with FSDP unshard hooks waiting only for chunks that own each module’s parameters. Trainers callwait_for_optimizer_stepbefore weight broadcasts so overlapped steps finish beforeempty_cache/ gathers.Docs in
scaling.mddescribe chunked AdamW andoffload_overlap_forward. A unit test checks late-first-gradient parameters match the non-offloaded AdamW reference.Reviewed by Cursor Bugbot for commit f829bc9. Bugbot is set up for automated code reviews on this repo. Configure here.
🤖 Generated with Claude Code