Repository navigation
Support GDN2 and TE CUDA graphs for recurrence offloading - #8308
Draft
WEIXILIN0307 wants to merge 24 commits into
Draft
WEIXILIN0307 wants to merge 24 commits into
WEIXILIN0307 wants to merge 24 commits into
Conversation
…t copy `ChunkOffloadHandler.reload()` enqueues its host-to-device copy on the caller's current stream -- which on the `tensor_pop` path is the compute stream -- with no ordering edge on the group's `_offload_event`, recorded on `d2h_stream`. Only `bulk_reload_group` waits on that event; `tensor_pop` calls `reload()` inline and bypasses the wait. A backward that reaches a group before any prefetch ran can therefore read the pinned staging buffer while the device-to-host copy that fills it is still in flight. The saved activation silently becomes stale buffer contents and the gradients computed from it are wrong. Nothing raises, and the loss curve still descends. This contradicts the feature's own documented design: "Before the group's backward, tensors are reloaded from CPU to GPU on `h2d_stream`, and the compute stream waits for the transfer to complete." The inline path is not rare. The prefetch trigger `on_group_start_backward` is the backward of the group-start marker, which autograd runs at the end of that group's backward, after every `tensor_pop` in it, so the reload LIFO has to be primed one group ahead by `pre_reload_last_layer`. During warmup nothing primes it: `should_bulk_offload` returns True unconditionally while `is_warmup`, and `tensor_pop` drained a group's tensors while leaving the group object on `_groups_to_reload`, so every `bulk_reload_group` re-targeted an already-drained tail and the priming branch was never reached. Measured on a 4-layer GPTModel with `--offload-modules core_attn`, the warmup iteration took the unordered inline path for all 24 of its reloads, with 0 prefetched. Add a `reload_pending` latch to `OffloadTensorGroup`, set when the group's device-to-host copy is enqueued and cleared by whichever reload consumes it. On the inline path, `tensor_pop` waits on the group's offload event on the compute stream and drops the group from the LIFO so the next prefetch advances. Keying the latch on group identity is necessary rather than waiting unconditionally: `_offload_event` is a single event per group, reused every iteration, so a group that was not offloaded this iteration would otherwise wait on a stale edge. `reset()` clears the latch for groups offloaded but never reloaded, and `bulk_reload_group` clears it too so a prefetched group never re-serializes compute behind the device-to-host copy. With no artificial stall at all -- a 256 MiB device-to-host copy and nothing else -- 20 trials per arm corrupted 19/20 on B300 and 20/20 on A40 before this change, and 0/20 on both after it. Add a regression test that stalls `d2h_stream` with `torch.cuda._sleep` before the commit to make the race deterministic, asserts the stall took effect, and then checks both the ordering and the reloaded values. Reverting the latch turns it red. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: GarlGuo <wg247@cornell.edu>
The single-group test stayed green when `_groups_to_reload.remove(group)` was dropped from `tensor_pop`. Use two forward-ordered groups, so the inline pop of the second must release the LIFO and let the next prefetch advance to the first. Raised in review on NVIDIA#7399. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: GarlGuo <wg247@cornell.edu>
6 of 7 tasks
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
WEIXILIN0307
force-pushed
the
feat/gdn2-te-graph-offload
branch
from
October 21, 2026 16:49
1f6eab9 to
8002cd3
Compare
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
WEIXILIN0307
force-pushed
the
feat/gdn2-te-graph-offload
branch
from
October 22, 2026 04:24
8002cd3 to
3b6f5e6
Compare
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
…PT training Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
This was referenced Oct 27, 2026
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
4 of 7 tasks
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
Signed-off-by: Pinoeer-kingxi <13022943007@163.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.
What does this PR do?
Extend opt-in
gdn_core_attnactivation offloading to BF16/FLA GDN2 training and Transformer Engine CUDA graphs for GDN and GDN2. The existing recurrence scope, backward prefetch anchor, transfer streams and pinned-memory pool are reused. TE layer-graph replay stays on the caller's autograd stream so shared graph pools remain ordered.GDN2 keeps sigmoid gate outputs resident when gate backward retains the same storage: the differentiable write gate and the differentiable erase gate without head expansion. This avoids transfers that cannot release the original GPU storage.
Attention and whole-layer TE graphs,
gdn_norm_outrecomputation and delayed offload are supported. Graph offload requires PyTorch >= 2.9, TE >= 2.14 and positive graph warmup; setNVTE_CPU_OFFLOAD_V1=1before launch. GDN2 requiresfla.ops.gdn2. FP8/FP4, full recomputation and local/full-iteration graphs are outside this PR; low precision is proposed in #8543. See configuration and usage.Dependencies
Merge #7399, #8243 and #7908 first. This branch also includes the independent mRoPE graph-input fix in #8555, needed for correct Qwen graph training. The original GDN2/graph feature diff excludes that separate fix. Composition with #7852 is not validated.
Validation
At
03b5ef353a1e657a89247f1ae0f67d05d619166f, including #8555 andmainat581248531984920e92f076946ad4629d359442a4:Current-head commands, rank logs, source checksums and audit. Earlier Qwen and two-rank GDN/GDN2 tests ran at
c5264b66197040f4309edfdb22dc9bbaf62189b1; their original logs remain available. Multi-rank and performance results have not been rerun on the current head.Memory and runtime
At earlier head
c5264b66197040f4309edfdb22dc9bbaf62189b1, BF16 whole-layer graph training on one RTX PRO 6000 used sequence 2048, four microbatches, fraction 1, the default threshold and no norm recomputation. Each fresh-process arm uses three warmup steps and ten measured optimizer steps; capture is timed separately. Peaks are medians of the per-arm peaks, and step changes are paired medians and ranges.The Qwen row uses five pairs because of timing variation; GDN2 uses three. These runs reduce reserved memory but not the measured allocated peak. No other compute process was observed on the measured GPU, but other GPUs were shared; host and PCIe isolation are not established. Commands, rank logs, samples, telemetry and audit.
A separate allocator snapshot at the same head and Qwen configuration locates the measured peak during output projection and cross-entropy: 970 MiB of logits and a 1940 MiB FP32 conversion are live together. In the snapshot after that step, the graph pool reserves 4132 → 3690 MiB, while its allocated count is 46 MiB in both arms. In the GDN2 snapshot, graph-pool reserve also decreases, while the total allocated peak increases by 1 MiB. These observations apply to the measured configurations.
As an exploratory follow-up, smaller offload fractions and the existing delayed-offload option did not reduce allocated peak in this configuration. The delayed-offload arm matches losses, gradients, weights and complete optimizer states for six steps; the timing samples are not sufficient to establish a performance improvement. Snapshot traces, commands, comparison data and audit.
Earlier A6000 measurements and a gate-selection experiment used different revisions and are retained in the historical graph-training evidence and profile. The historical Qwen graph run omitted mRoPE and is not a valid correct-training performance result.
The option remains disabled by default.
Issue tracking
Related to #6757.
Code provenance
No code copied or adapted from external repositories.
Contribution process
Pre-checks