Repository navigation
feat(weights): sparse deltas from the full-offload optimizer step, without a snapshot - #4135
Merged
faresobeid merged 1 commit intoOct 17, 2026
Conversation
faresobeid
merged commit Oct 17, 2026
35298cb
into
feat/sparse-filesystem-weight-broadcast
7 checks passed
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.
Stacked on #4085 (sparse filesystem weight broadcast). With
model.offload = "full"(AdamW, sign_sgd), the RL trainer takes the sparse delta from the optimizer step itself, instead of diffing its weights against a snapshot of the previous broadcast.How it works
FullCPUOffloadOptimizergets an optionalweight_hook(local_param, source), called on its H2D stream in place oflocal_param.copy_(source). At that point the local shard still holds the previous broadcast's weights, andsourceis the chunk's new values in the pinned output slot.wait_for_weight_reads). For every trainer tensor stored in a parameter, the hook copies the old and new values into one of two GPU slots, writes the new values to the parameter, and queues the slot to a diff thread. The diff thread runs feat(weights): sparse XOR deltas for the filesystem weight broadcast #4085's diff code on the slot: bf16 raw-bit differences, or for fp8 engines the AOTI quantizer on old and new plus_scale_invrecords, then zigzag, index gaps and checksums of the new values. The GPU parts are held until the step ends. A tensor may be a strided view of a parameter (for examplegate_proj/up_projof the packedgate_up_proj).broadcast()hands the parts to the writer (D2H, part files, manifest, markers). The delta format, the engine apply path and the per-step checksums are unchanged.pwrites the new values from the pinned slot into the anchor files at precomputed offsets. Slices go round-robin to the ~1 GiB files, and each parameter's slices are written by 8 threads. One file is written at ~0.5 GiB/s on /home; 4 files at once reach 1.9 GiB/s. The files are written next to the step dir and moved in when the step is published, so an anchor holds exactly one version. There is no host buffer and no GPU-to-host anchor copy.q/k/v_projandrouter.gateon Qwen3-30B (0.94 GiB per rank);optimizer,none, Muon) and SFT keep feat(weights): sparse XOR deltas for the filesystem weight broadcast #4085's snapshot path unchanged. Hooked deltas need a broadcast after every optimizer step: a broadcast for a step whose pass was not hooked raises.Results
Qwen3-30B-A3B-Instruct-2507, H200, 1 node (6 trainer GPUs with
offload = "full", engine TP2), reverse-text, 20 steps, anchors every 3 steps. Each pair ran on the same node. Medians over steps 3-20. Every delta was verified by the engines in every run (40/40, no divergence).