Skip to content

Overlap experimental FSDP communication with compute#5719

Open
wujingyue wants to merge 2 commits into
NVIDIA:mainfrom
wujingyue:mfsdp-prefetch-on-5513
Open

Overlap experimental FSDP communication with compute#5719
wujingyue wants to merge 2 commits into
NVIDIA:mainfrom
wujingyue:mfsdp-prefetch-on-5513

Conversation

@wujingyue

@wujingyue wujingyue commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This implements the “explicit prefetching” approach used in MFSDP v1 for experimental Megatron-FSDP (mfsdp_v2). The current implementation assumes that the static module registration order matches the execution order.

Each module’s pre-forward hook prefetches the next module’s parameter all-gather. During backward, the same order is traversed in reverse: each pre-backward hook prefetches the preceding module’s all-gather, which is the next one needed in backward.

An example trace from the added unit test:

image

Implementation

Forward prefetch

After materializing the current FSDP unit's parameters, pre_forward issues the next unit's all-gather on the shared all-gather stream. Each unit releases its own unsharded storage in stream order after its compute consumer completes, bounding the live parameter storage to the current unit and its prefetched successor.

Backward overlap

The shared FSDP context reuses its static forward_order in reverse. Each backward pre-hook:

  1. waits only for that unit's prefetched all-gather;
  2. prefetches the next unit needed by backward on the all-gather stream;
  3. allows that all-gather to overlap the current unit's gradient computation.

Gradient reduction is split into allocation, packing, and communication stages. Once a module's gradients are packed, its reduce-scatter is launched immediately on a separate reduce-scatter stream, allowing it to overlap subsequent backward GEMMs and all-gathers. Existing symmetric-memory scaling, mixed gradient dtypes, first-backward assignment, and microbatch accumulation semantics are preserved.

An autograd-end callback registered by the root makes the caller's current stream wait for the reduce-scatter stream before backward() returns. This cannot rely on the root module's own post-backward hook: that hook tracks gradients for parameters owned by the root and may run before descendants finish, or may not run at all when the root owns no trainable parameters.

Verification

  • Experimental FSDP v2 numerical, accumulation, mixed-precision, storage-release, symmetric-memory, and memory tests pass with two CUDA ranks: 16 passed per rank.
  • The profiler test covers one forward and backward pass. For four child FSDP units, it requires all 6 expected all-gather/GEMM overlaps and all 3 expected reduce-scatter/GEMM overlaps, rather than accepting any single overlap.
  • The profiler test also verifies that all-gathers use one stream, reduce-scatters use another, and both streams are distinct from compute.
  • A local Nsight Systems capture confirms backward all-gathers and reduce-scatters overlap GEMM execution on separate streams.
  • Formatting, import ordering, lint, byte-compilation, and diff checks pass.

@copy-pr-bot

copy-pr-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@wujingyue wujingyue changed the base branch from pull-request/5513 to main July 9, 2026 03:09
@wujingyue wujingyue force-pushed the mfsdp-prefetch-on-5513 branch from 356bf43 to 2d43993 Compare July 9, 2026 04:01
@copy-pr-bot

copy-pr-bot Bot commented Jul 9, 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.

@wujingyue wujingyue force-pushed the mfsdp-prefetch-on-5513 branch from 2d43993 to 700c4f0 Compare July 9, 2026 04:59
@wujingyue wujingyue changed the title Prototype: forward all-gather prefetch in experimental Megatron-FSDP (stacked on #5513) Prototype: forward all-gather prefetch in FSDP Jul 9, 2026
@wujingyue wujingyue force-pushed the mfsdp-prefetch-on-5513 branch from 700c4f0 to 68aa5a2 Compare July 9, 2026 06:01
with torch.cuda.device(device):
self.allgather_stream = torch.cuda.Stream()

def next_of(self, module: "FsdpModule") -> "FsdpModule | None":

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Consider a linked hashmap.

Comment on lines +140 to +141
# named_modules() yields units in registration order, which is the static
# forward execution order used to prefetch the next unit's all-gather.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a fragile assumption. Fine for the prototype.

@wujingyue wujingyue force-pushed the mfsdp-prefetch-on-5513 branch from 9045a0d to 4d2f7de Compare July 9, 2026 23:26
wujingyue added 2 commits July 9, 2026 23:28
In pre_forward, issue the next FSDP unit's all-gather on the comm stream
(static module-tree order) so AG_{i+1} launches before F_i finishes, instead
of relying on delayed releases for overlap.

Since prefetch now drives overlap, the delayed-release deque is dropped: each
unit frees its own unsharded storage on the all-gather stream right after its
own compute (event-gated on a compute-stream consumer event so the free never
races the consuming kernels), rather than queuing releases. This bounds peak
in-flight memory to the current unit plus the prefetched next.

Each unit tracks its own materialized state via FsdpModule._is_unsharded, so
pre_forward can skip a unit an earlier unit already prefetched -- no central
id-keyed set or per-forward reset.

Applied on top of the FSDP all-gather overlap work (NVIDIA#5513). Verified against the
mfsdp_v2 unit tests: no new failures vs the base, and forward-peak-memory-bounds
now passes (it failed with the deque). resting-forward (32 MB) and flaky-overlap
fail on the base too. Profiling: prefetch raises concurrent AG/compute overlap
but not forward wall-clock because the SM-based all-gather contends with the
persistent full-occupancy GEMM; the real lever is SM-free (CTA-zero) comm.
Static module order assumes forward order == registration order.

Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Reuse the recorded forward order in reverse during backward to prefetch each upcoming FSDP unit's parameters on the all-gather stream. Wait only for the current unit before compute so later all-gathers can overlap backward GEMMs.

Split gradient reduction into allocation, packing, and reduce-scatter stages, and launch each reduction immediately on a dedicated stream. Register an autograd-final callback from the root backward pre-hook to order optimizer work after communication while preserving mixed-dtype accumulation and symmetric-memory averaging.

Extend the profiler test to require the expected all-gather and reduce-scatter overlap counts and verify that communication and compute use distinct streams.

Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
@wujingyue wujingyue force-pushed the mfsdp-prefetch-on-5513 branch from 4d2f7de to 29f3f1f Compare July 9, 2026 23:35
@wujingyue wujingyue changed the title Prototype: forward all-gather prefetch in FSDP Overlap experimental FSDP communication with compute Jul 9, 2026
@wujingyue wujingyue marked this pull request as ready for review July 9, 2026 23:40
@wujingyue wujingyue requested review from a team as code owners July 9, 2026 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MFSDP v2 overlap, prefetching, and double-buffering schedule support

2 participants