Skip to content

Fix forward chunk tracking for resident activation groups - #8243

Open
WEIXILIN0307 wants to merge 12 commits into
NVIDIA:mainfrom
WEIXILIN0307:fix/activation-offload-lifecycle
Open

WEIXILIN0307 wants to merge 12 commits into
NVIDIA:mainfrom
WEIXILIN0307:fix/activation-offload-lifecycle

Conversation

@WEIXILIN0307

@WEIXILIN0307 WEIXILIN0307 commented Oct 15, 2026 •

Copy link
Copy Markdown
  • I, the PR author, have personally reviewed every line of this PR.

What does this PR do?

Fix a Chunk mismatch assertion during fixed-length GPU training when fine-grained activation offloading is enabled with --activation-offload-fraction 0. The failure was reproduced in a native four-layer GPT training test with synthetic tokens and two microbatches.

finish_all_groups() treats empty offload/reload queues as a completed forward chunk. At fraction zero, those queues can be empty while later groups of the same scope still remain. Advancing the handler at that point gives backward the wrong microbatch's chunk.

Remove the queue-empty shortcut and use the existing remaining-group lookup and cursor. The regression runs the real forward/backward schedule and optimizer steps, starting with the normal offload warmup lifecycle. Evaluation-first initialization and changes to tensor sizes after warmup are outside this PR.

Dependencies and scope

Requires #7399 (73ac08c254a0ff8997a0ff559799a4d7f073248b) for fallback reload ordering and reload-LIFO bookkeeping. Its original commits are preserved; merge #7399 first. #7908 depends on this PR.

The original change after that prerequisite contains the chunk-tracking fix and its regression.

Validation

Current head: 5bc9e72bda70129f088dd78328d4064f1d838c62, including a merge from main at 3ddfe0fe07efc73dbdc40d4a02acbc1c1f8a9f08.

On one RTX PRO 6000, seven selected tests pass: the native fixed-length training regression at fractions 0/.5/1, three TE CUDA graph cases, and the upstream checkpoint-without-output storage regression. Commands and rank logs are available.

At d8be77f56f4d38713cb0306c5c4893aa3cb2331a, the same fixed-length regression fails with Chunk mismatch on the unchanged prerequisite at fraction zero and passes with the fix. Both positive-fraction controls pass. The pinned original evidence retains commands, per-rank logs, state comparisons and two-/four-rank results. Those multi-rank executions are historical; the current-head rerun uses one GPU. Upstream GPU CI is pending.

Issue tracking

Related to #6757.

Code provenance

No code copied or adapted from external repositories.

Contribution process

Pre-checks

  • I have added relevant unit tests.
  • I have added relevant functional tests.
  • GPU kernel determinism registration: not applicable; no numerical kernel changes.
  • I have kept the change within existing manager interfaces.
  • I have documented the trigger and validation scope.
  • I have run formatting and lint checks on the changed files.

GarlGuo and others added 4 commits September 16, 2026 10:49
…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>
@copy-pr-bot

copy-pr-bot Bot commented Oct 15, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@WEIXILIN0307
WEIXILIN0307 force-pushed the fix/activation-offload-lifecycle branch from fc9e737 to af95692 Compare October 15, 2026 07:54
@WEIXILIN0307
WEIXILIN0307 marked this pull request as ready for review October 16, 2026 13:03
@WEIXILIN0307
WEIXILIN0307 requested review from a team as code owners October 16, 2026 13:03
@svcnvidia-nemo-ci
svcnvidia-nemo-ci requested a review from a team October 16, 2026 13:03
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Oct 17, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added waiting-on-customer Waiting on the original author to respond and removed waiting-on-maintainers Waiting on maintainers to respond labels Oct 20, 2026
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
@WEIXILIN0307
WEIXILIN0307 force-pushed the fix/activation-offload-lifecycle branch from af95692 to fb271eb Compare October 21, 2026 16:49
@WEIXILIN0307 WEIXILIN0307 changed the title Fix activation offload warmup and pooled transfer ordering Fix activation offload warmup and forward chunk progression Oct 21, 2026
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
@WEIXILIN0307
WEIXILIN0307 force-pushed the fix/activation-offload-lifecycle branch from fb271eb to d8be77f Compare October 22, 2026 04:24
@svcnvidia-nemo-ci svcnvidia-nemo-ci removed the waiting-on-customer Waiting on the original author to respond label Oct 22, 2026
@WEIXILIN0307 WEIXILIN0307 changed the title Fix activation offload warmup and forward chunk progression Fix forward chunk tracking for resident activation groups Oct 22, 2026
@WEIXILIN0307

Copy link
Copy Markdown
Author

Thanks for tracing the scheduler and pool lifetimes so carefully. I’ve removed the extra H2D-to-D2H wait and the test that bypassed normal warmup. I’ve also made #7399 a prerequisite and removed my duplicate fallback wait. Reload ordering and queue bookkeeping now come from that PR.

I took another look at the remaining changes and removed the evaluation-first reset handling, the tensor-growth special case, and the new scheduler argument. The evaluation-first test only showed an internal state difference, without demonstrating a later training failure. Tensor growth also goes beyond what this PR needs.

The fix I’ve kept is in finish_all_groups(): empty transfer queues don’t mean that the current microbatch has finished its forward groups. With fraction zero, that shortcut can advance to the next chunk too early and cause Chunk mismatch during backward.

The new regression uses fixed-length training from the start: sequence 128, two microbatches and three SGD steps. The old code fails at fraction zero on both ranks, while fractions 0.5 and 1 pass. The updated code passes all three.

I also reran the dependent GDN tests and complete Qwen TP2+SP+PP2 training on four A6000s. The logs and commands are here, including the old-code failures and exact commits. #7399 still needs to land first.

@lhb8125

lhb8125 commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

/ok to test 1336c9d

@lhb8125

lhb8125 commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

/review

@svcnvidia-nemo-ci svcnvidia-nemo-ci left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. No actionable findings in this light review. Inspected chunk progression, inline-reload ordering and queue bookkeeping, and the regression tests. Static review only; GPU tests were not run.

Note: formal approval is not available for this request, so this review is posted as a comment only.

Review generated by codex. This was a full review. Request a full review with /review reset=true.

@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Oct 25, 2026
@lhb8125

lhb8125 commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

/ok to test f9d0b7a

@lhb8125 lhb8125 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@svcnvidia-nemo-ci svcnvidia-nemo-ci added waiting-on-customer Waiting on the original author to respond and removed waiting-on-maintainers Waiting on maintainers to respond labels Oct 26, 2026
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
…PT training

Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
@WEIXILIN0307
WEIXILIN0307 requested review from a team as code owners October 26, 2026 15:49
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the Final Review PR is in the "final review" stage label Oct 27, 2026
Signed-off-by: Pinoeer-kingxi <13022943007@163.com>
@svcnvidia-nemo-ci svcnvidia-nemo-ci added waiting-on-maintainers Waiting on maintainers to respond and removed waiting-on-customer Waiting on the original author to respond labels Oct 29, 2026

This branch was previously deployed

2 inactive (outdated) deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-request Final Review PR is in the "final review" stage waiting-on-maintainers Waiting on maintainers to respond

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants