Repository navigation
Peng add cpu adam fused step v1 - #8841
Draft
pengdurice wants to merge 2 commits into
Draft
pengdurice wants to merge 2 commits into
pengdurice wants to merge 2 commits into
Conversation
…ed buffers Signed-off-by: pengdurice <pengduhit@gmail.com>
…ide the CPU Adam pass Signed-off-by: pengdurice <pengduhit@gmail.com>
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.
ZeRO-3 CPU offload: apply the clip factor and write the bf16 copy inside the CPU Adam pass
Builds on: #TODO(pinned copy-back PR number) (ZeRO-3: copy offloaded master weights back through pinned buffers).
This branch contains that PR's commit followed by this one; review the second commit.
Part of: #8672 (Q4 2026 roadmap), "Enable training of large scale models".
Summary
With the optimizer offloaded to the CPU, ZeRO-3's step makes three passes over every CPU sub-group:
grad.mul_), even when the factor is 1;DeepSpeedCPUAdam(reads gradients, parameters and both moments; writes parameters and both moments);This PR folds 1 and 3 into 2: the CPU Adam kernel multiplies each gradient by the factor as it loads it, and writes
the bf16 copy of each updated piece into a pinned staging buffer, which is copied to the device while the next piece
is updated. The parameters after training are bitwise equal to the three-pass path.
Duplicate check
Searched open and recent pull requests on 2026-10-15 ("cpu adam", "offload", "pinned", "master weights"): no PR
fuses the clip factor or the copy-back into the CPU Adam step. Related but different: #8673 fuses mixed-precision
gradients and the parameter writeback into the GPU
FusedAdam; #8813 builds the CPU optimizer ops without CUDA andfixes the CPU Lion update; #8817 adds an RVV kernel to CPU Adam (both change the same C++ files, so whichever merges
later rebases); #8719 adds asynchronous CPU-offloaded optimizers.
The change
csrc/includes/cpu_adam.h_grad_scale,_params_copy,set_grad_scale,set_params_copy,params_copy_for: the step multiplies each loaded gradient by the factor and, when a copy buffer is set, stores the updated parameter in bf16 therecsrc/includes/sve.hcsrc/adam/cpu_adam_impl.cppds_adam_step_scaled,ds_adam_step_scaled_copy,ds_adam_copy_supported,ds_adam_block_numelcsrc/adam/cpu_adam.cppadam_update_scaled,adam_update_scaled_copy,adam_copy_supported,adam_block_numeldeepspeed/ops/adam/cpu_adam.pystep(closure=None, grad_scale=1.0);copy_supported();step_pieces(piece_numel, before_piece, after_piece, grad_scale=1.0)updates a parameter piece by piece,before_piecereturns the bf16 buffer for the piece,after_piecestarts its copy; the piece size is rounded down to a multiple of the kernel's widest vector block (adam_block_numel, 128 floats with AVX512) so every element takes the vector path, as in one call on the whole tensordeepspeed/runtime/zero/stage3.py_grad_scale_for_cpu_adampasses the factor to CPU Adam instead ofmul_for CPU sub-groups;_cpu_adam_step_and_copy_backrunsstep_piecesinto the two pinned staging buffers of the copy-back PR, waits for the previous copy out of a buffer before refilling it, and waits for the last piece before returning (the next forward's all-gathers do not wait for this stream)tests/unit/ops/adam/test_cpu_adam.pytests/unit/v1/zero/test_zero.pyWhere the fused path runs: CPU sub-groups with float32 gradients and master weights,
pin_memoryon, a bf16 devicepartition, and a CPU Adam build with a bf16 store (AVX512, NEON and the scalar loop;
copy_supported()asks the build).Everything else (fp16 or a GradScaler, ZenFlow, NVMe offload, Muon, SuperOffload,
pin_memoryoff) keeps the currentpath. No configuration key changes.
Measured
GLM-5.2, real weights, PG-19 rows of 65,536 tokens, fp32 Adam states, CPU optimizer offload, each sequence over 2 GPUs.
Profile (one step, rank 1): the separate
aten::mul_pass took 824 ms of a 6.70 s step on 2 nodes before;with this PR it is 1.2 ms in an 8-node step, and no separate cast pass remains after the backward.
Memory: no new buffers (the staging buffers belong to the copy-back PR).
This change was also in the DeepSpeed tree of our MiniMax-M3 and GLM-5.2 runs against Megatron-LM (real weights,
65,536-token sequences, 80-95% of the optimizer on the CPU): MiniMax-M3 on 4 nodes 85.24 s per step vs 119.35 s for
Megatron-LM (one job on the same nodes); GLM-5.2 first 38 layers on 4 nodes 62.03 vs 63.73 s (one job on the same nodes);
GLM-5.2 all 78 layers on 8 nodes 130.58 vs 135.56 s (separate jobs on 7 of the same 8 nodes). On one node, master plus this
PR, the copy-back PR and the two model PRs (#8658, #8659) gives the same loss at every step as that tree.
Tests
test_cpu_adam_grad_scale_matches_scaling_in_placestep(grad_scale=f)gives bitwise the parameters and moments ofgrad.mul_(f)thenstep()test_cpu_adam_step_pieces_matches_stepstep_pieceswith pieces of 1,000 elements (not a multiple of the vector block) and 524,289-element tensors gives bitwise the result of onestep(), and the bf16 copies equalparam.to(torch.bfloat16)TestZero3CpuAdamGradScale(2 ranks; clip 1e-3 and 1e6; ratio 1.0 and 0.5)TestZero3CpuAdamCopyBack(2 ranks; ratio 1.0 and 0.5)Hardware: 1 node of 8 H200 (143,771 MiB, driver 580.126.09), torch 2.8.0+cu126, pytest 9.1.1:
The 5 skips are the file's own fp16 cases.
pre-commit run --filespasses on every changed file.Compatibility
pin_memoryon, bf16, all of the optimizer on the CPU (ratio 1.0) and half (0.5)copy_supported()is false and the copy keeps the old path; the clip factor is fused on every build)pin_memoryoffAI assistance
This change, its tests and this description were prepared with AI assistance (Claude).