Skip to content

fix(megatron-fsdp): compute SWiGLU/GDN split in item coordinates for non-DTensor optimizer states#4423

Open
xuwchen wants to merge 2 commits into
NVIDIA:mainfrom
xuwchen:fix_mfsdp_ckpt_save
Open

fix(megatron-fsdp): compute SWiGLU/GDN split in item coordinates for non-DTensor optimizer states#4423
xuwchen wants to merge 2 commits into
NVIDIA:mainfrom
xuwchen:fix_mfsdp_ckpt_save

Conversation

@xuwchen

@xuwchen xuwchen commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

What does this PR do ?

split_swiglu_linear_fc1 and split_gdn_fused originally used data.numel() // tp_mesh.mesh.numel() to compute component slices (w_slice/v_slice for SWiGLU, comp_slice for GDN), then intersected them with fsdp_slice to determine each rank's portion. When data is a DTensor, data.numel() returns the global element count (=param.numel()), so component slices and fsdp_slice are both in item coordinates and the intersection is valid. However, when data is a plain Tensor (optimizer states created by FusedAdam), data.numel() returns the local shard size on this rank, making the component slices's range far smaller than the item coordinate space and resulting in an intersection across incompatible coordinate systems.

The fix replaces data.numel() with dist_param.numel() (dist_param is always a DTensor and therefore reflects the global element count), ensuring fsdp_slice and all component slices are in item coordinates.

Contribution process

Pre-checks

  • I have added relevant unit tests
  • I have added relevant functional tests
  • I have added proper typing to my code Typing guidelines
  • I have added relevant documentation
  • I have run the autoformatter.sh on my PR

Code review

Feel free to message or comment the @mcore-oncall to help accelerate your merge into main. The less complex your PR is, the faster it will be approved and merged!

All PRs start as draft. If you open a non-draft PR, it will be automatically converted to draft.

Step 1: Mark PR as "Ready for Review"

  1. When your PR is ready, click Ready for Review.
  2. An oncall reviewer is auto-assigned and expert reviewers are notified based on your changes.
    • Some PRs may jump straight to step 2. This is determined by .github/CODEOWNERS.

⚠️ Only mark as ready once merge-conflicts are resolved and the CI is passing.
Final Review might get declined if these requirements are not fulfilled.

Step 2: Final Review

For PRs that change megatron/core, once all expert reviewers have approved, the Final Review label is applied automatically and final reviewers are assigned.

For PRs outside megatron/core, this step is skipped.

Step 3: Approved

Once all required reviewers have approved, the Approved label is applied automatically.

Merge

Any member of mcore-engineers will be able to merge your PR.

For MRs into `dev` branch The proposed review process for `dev` branch is under active discussion.

MRs are mergable after one approval by either eharper@nvidia.com or zijiey@nvidia.com.

@xuwchen xuwchen requested review from a team as code owners April 22, 2026 13:59
@copy-pr-bot

copy-pr-bot Bot commented Apr 22, 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.

@svcnvidia-nemo-ci svcnvidia-nemo-ci marked this pull request as draft April 22, 2026 13:59
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been automatically converted to draft because all PRs must start as drafts.

When you are ready for review, click Ready for Review to begin the review process. This will:

  1. Add the oncall reviewer (optional reviewer)
  2. Add required review teams based on your changes

See the contribution guide for more details.

@cspades cspades left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is a versatile / good change, just curious about why you came across this error as I did! (See my comment about the TransformerEngine bug.)

Comment on lines -277 to 296
assert data.shape[swiglu_shard_axis] % 2 == 0, (
f"SWiGLU weights must have an even size along the shard axis {swiglu_shard_axis}, "
f"got {data.shape[swiglu_shard_axis]}"
# Use dist_param (always a DTensor) for global shape/numel,
# as data may be a regular Tensor (e.g., optimizer states).
global_shape = dist_param.shape
if isinstance(data, DTensor):
assert data.shape == global_shape, (
f"DTensor shape mismatch: data.shape={data.shape} vs "
f"dist_param.shape={global_shape}"
)
assert global_shape[swiglu_shard_axis] % 2 == 0, (
f"SWiGLU FC1 must have even global size along axis {swiglu_shard_axis}, "
f"got {global_shape[swiglu_shard_axis]} (global_shape={list(global_shape)})"
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So basically, if data is a DTensor, we should ensure it matches the state DTensor. And then, we always use the global shape. (I've hit this error before when FusedAdam was broken, which I fixed here: NVIDIA/TransformerEngine#2795)

When would data not be a DTensor in the context of this function? 👀 Is it a bug or just so this function can be used outside of MLM / MBridge checkpointing?

@xuwchen xuwchen Apr 28, 2026

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.

Good point! Your fix in NVIDIA/TransformerEngine#2795 would indeed resolve this issue by making FusedAdam optimizer states (exp_avg, exp_avg_sq) DTensors, so data would always be a DTensor and the original code would work correctly.

The case we've hit so far is specifically FusedAdam producing plain Tensor optimizer states. I'm not 100% sure whether other code paths could also pass plain Tensors to this function, but since split_swiglu_linear_fc1 doesn't enforce any type constraint on data, I think it's safer to keep this fix on the MCore side as defensive programming. The DTensor case is the "easy" case, we just assert shape consistency there. The real fix ensures that even if data is a plain Tensor, the W/V split boundaries are computed in the correct (global) coordinate system.

I've attached a visualization based on a deepseek proxy model experiment to help illustrate this. Hope this clarifies the motivation behind the fix.

swiglu_checkpoint_bug

@xuwchen

xuwchen commented May 15, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test e6aa004

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