Skip to content

Record and replay runtime prefetch order for MFSDP v2 - #7998

Draft
shjwudp wants to merge 20 commits into
NVIDIA:mainfrom
shjwudp:feat/mfsdp-v2-trace-replay-standalone
Draft

shjwudp wants to merge 20 commits into
NVIDIA:mainfrom
shjwudp:feat/mfsdp-v2-trace-replay-standalone

Conversation

@shjwudp

@shjwudp shjwudp commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Record actual MFSDP v2 parameter demands over a complete iteration, preserving repeated module calls and mixed forward/backward phases. This builds on the merged ExecutionOrder change (#8207) and brings the iteration-scope implementation from #8284 into this PR.

Changes

  • Keep ExecutionOrder generic: callers store weak module references or (weakref(module), phase) pairs. Traversal uses advance() and an explicit reset, without module lookup or _unique_index.
  • Add iteration(context, key=..., rerecord=...). The first successful scope records every demand; subsequent scopes validate replay and prefetch through a separate monotonic cursor. Failed or divergent iterations invalidate their cached order.
  • Preserve static forward/backward prefetch without a recording scope. Root hooks reset the static cursors at the first root; active iteration scopes own their cursor lifecycle.
  • Scope the VPP and combined 1F1B entry points, and issue phase-aware demands from the combined hooks.
  • Retain --megatron-fsdp-record-prefetch-order for other eager training schedules. Training and validation share the same scope wrapper, with separate cache keys for their schedule configurations.
  • Record repeated backward invocations while opening a shared module's backward phase and NVTX range once, preserving the current gradient-completion lifecycle.

Usage and limits

For ordinary eager training, enable --use-megatron-fsdp --megatron-fsdp-version 2 --megatron-fsdp-record-prefetch-order --cuda-graph-impl none. Static forward/backward prefetch continues to work without this flag. Eager VPP and combined 1F1B enter the scope at their schedule boundaries.

Recorded schedules must be repeatable and collective-compatible across peers. Explicit scope keys or rerecord=True handle changed execution patterns. CUDA graph recording/replay is outside this change.

Work in progress: an earlier combined VPP experiment stalled during replay, including in its eager reference. That issue has not been resolved or revalidated on this revision; the presence of iteration scopes does not establish combined VPP correctness.

Validation

  • Eight local source-level checks passed for occurrence traversal, prefetch budgets, mixed-phase recording/replay, invalidation, and weak ownership. These checks isolate the order logic and mock communication; they are not distributed GPU tests.
  • Import sorting, Black 26.3.0 (core/tests), Ruff, syntax parsing, and whitespace checks passed.
  • Preserved the distributed shared-projection regression comparing outputs, gradients, and SGD updates across recording and replay. Added iteration/checkpoint coverage and retained existing context regressions.
  • GPU validation of this revision is pending. The source transfer to CW timed out, after which both CW and EOS login hostnames stopped resolving. No GPU allocation was created.

@copy-pr-bot

copy-pr-bot Bot commented Oct 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.

@shjwudp shjwudp changed the title Add opt-in trace-and-replay scheduling for experimental MFSDP Add opt-in trace-and-replay scheduling for MFSDP v2 Oct 9, 2026

@wujingyue wujingyue 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.

Hey @shjwudp,

It's amazing to see how quickly you can prototype it on V2!

If you want this to be more than a prototype, I'd recommend have your first PR targeting just recording the prefetch module order. That should be easier and the PR should be much simpler, and we know recording module orders is needed right now. Then, on top of that, you can draft-add the "compile" and "replay", and verify performance benefit on your customer models.

How does that sound to you?

@shjwudp shjwudp changed the title Add opt-in trace-and-replay scheduling for MFSDP v2 Record MFSDP v2 prefetch module order Oct 10, 2026
@shjwudp shjwudp changed the title Record MFSDP v2 prefetch module order Add opt-in trace-and-replay scheduling for MFSDP v2 Oct 10, 2026
@shjwudp
shjwudp force-pushed the feat/mfsdp-v2-trace-replay-standalone branch from 234d9c5 to 95ccb5c Compare October 10, 2026 08:11
@shjwudp shjwudp changed the title Add opt-in trace-and-replay scheduling for MFSDP v2 Record and replay runtime prefetch order for MFSDP v2 Oct 10, 2026
@shjwudp
shjwudp marked this pull request as ready for review October 10, 2026 16:58
@shjwudp
shjwudp requested review from a team as code owners October 10, 2026 16:58
@shjwudp

shjwudp commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author
  • Add --megatron-fsdp-record-prefetch-order to record the first executed training iteration, including all microbatches. Shared contexts across model chunks are entered once.

Hi @wujingyue , I’ve updated the code to only record the module execution order during the forward and backward passes. What do you think of this approach? It still needs testing, but this is the general direction I’m planning to take.

@wujingyue wujingyue 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.

Thanks for the PR!

@shjwudp
shjwudp requested a review from wujingyue October 13, 2026 14:42

@wujingyue wujingyue 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.

Inlined

Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jingyue Wu <jingyuew@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
@shjwudp
shjwudp requested review from a team as code owners October 21, 2026 04:59
Unify static and recorded execution orders using weak module references and execution phases. Capture one eager training iteration for VPP and combined 1F1B, then replay it through the existing parameter prefetch traversal. Preserve demand-only validation and static prefetch outside runtime capture.

Signed-off-by: Jack Chang <jianbinc@nvidia.com>

@wujingyue wujingyue 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.

Thanks for the PR!

Several changes are a bit controversial at this very moment. Can we extract the non-controversial part into a separate PR so it's not helped up in review for too long? https://artsy.github.io/blog/2021/03/09/strategies-for-small-focused-pull-requests/

For example, I'd first target the standard (non-PP, non-VPP, non-1f1b) use case where static order mismatches execution.

ExecutionEvent = tuple[ref["FsdpModule"], ExecutionPhase]


class IterationOrder(ExecutionOrder[ExecutionEvent]):

@wujingyue wujingyue Oct 21, 2026 •

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.

This appears to be an over-engineering that agents are "great" at :)

I don't think inheritance is the right design pattern. We could store the states in FsdpContext or if that becomes too complicated wrap them in a class with ExecutionOrder as an attribute.

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.

For the "store the states in FsdpContext". I’m wondering whether FsdpContext grow so much state could itself be a concern.

My intention was to introduce an IterationOrder abstraction, so I had the agent draft this class. Are you suggesting that IterationOrder should encapsulate an ExecutionOrder instance instead?

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.

It's OK to keep IterationOrder, but I'd prefer composition over inheritance. Something like

class IterationOrder:
  execution_order: ExecutionOrder
  cursors

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.

Hi Jingyue,

In the latest implementation, I’ve moved it into the context. Does this placement look reasonable to you? Relevant changes

I’ve also merged forward_order and backward_order into execution_order. The IterationOrder implementation is now split into two parts: #8485 is the first part, and #8486 is the second.

self.backward_start = 0
self.captured = False
self.active = False
self.mode: Literal["static", "record_replay", "demand"] = "static"

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.

Make an enum

self.backward_start = 0
self.captured = False
self.active = False
self.mode: Literal["static", "record_replay", "demand"] = "static"

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.

What's "demand" and should it be a separate PR so this PR stays small and focused? https://google.github.io/eng-practices/review/developer/small-cls.html#what-is-small

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.

To clarify, demand mode disables prefetching entirely. This differs from static, where the module execution order is specified at initialization, and record_and_replay, where the order is captured at runtime and replayed for prefetching. It serves as a fallback when prefetching is not used.

Currently, this mode is intended for validation and forward-only execution.

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.

Got it. We have arguments for prefetch sizes. Can they be used instead to disable prefetching?

def unshard(self, prefetch: Literal["forward", "backward", "none"] = "none") -> None:
# Independent roots can share a context even without VPP, e.g.
# separately sharded siblings whose parent is not sharded. Reset only
# at the first root so later roots continue the shared static cursor.

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.

I understand the English but I don't understand the problem it tries to solve. Again, a common problem of agent-generated code comments :)

I'd let the agent start with the simple alternative of letting one context stay in one mode, and then try to prove the complication is needed on top of that.

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.

Thanks for the feedback!

The comment was intended to explain the logic in the following three lines code: when using static prefetching with VPP, we need to call reset_cursor. Since VPP involves multiple model chunks, we need to identify the root module of the first VPP model chunk and call reset_cursor there.


@contextmanager
def _mfsdp_iteration(model, forward_only=False):
"""Manage one shared context across a complete scheduler invocation.

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.

Again, I understand the English but not the reason behind it.

I got your earlier point that recording needs a stop signal, but this iteration scope seems to do something else that I've yet to understand the motivation of.

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.

Currently, there are two modes require stop signals at different boundaries.

In module.py, the signal is used for static prefetching. In mfsdp_iteration.py, I define a separate iteration scope that spans multiple module hook invocations, with the stop signal tied to the end of that iteration scope.

@shjwudp
shjwudp marked this pull request as draft October 23, 2026 05:34
Select demand-only gathering before graph construction and retain that mode for graph calls outside scheduler scopes. Preserve active iteration scopes and existing root stream dependencies. Cover repeated VPP calls, out-of-scope graph warmups, nesting, and adapter mode selection.

Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>
Signed-off-by: Jack Chang <jianbinc@nvidia.com>

This branch was successfully deployed

1 inactive (outdated) deployment
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.

3 participants