Skip to content

Fix layer-wise distributed optimizer MXFP8 wgrad failure by padding per-param start - #4889

Closed
Wohox wants to merge 2 commits into
NVIDIA:mainfrom
Wohox:wohox/fix_muon_mxfp8_padding_main
Closed

Fix layer-wise distributed optimizer MXFP8 wgrad failure by padding per-param start#4889
Wohox wants to merge 2 commits into
NVIDIA:mainfrom
Wohox:wohox/fix_muon_mxfp8_padding_main

Conversation

@Wohox

@Wohox Wohox commented May 20, 2026

Copy link
Copy Markdown
Contributor

What does this PR do ?

Pad each parameter's start index up to a 64-element boundary in the default _ParamAndGradBuffer layout when the layer-wise distributed optimizer (Muon and other non-Adam/SGD optimizers) is active, so that main_grad slices reach the cuBLASLt-required D-pointer alignment for MXFP8 wgrad. Without this, mid-bucket params land at 16-byte D-pointer alignment and cublasLtMatmulAlgoGetHeuristic returns CUBLAS_STATUS_NOT_SUPPORTED, killing the training job.

This is the main-branch counterpart of #4840 (dev branch). The semantics are identical; the diff is rebased onto main and the plumbing site is the wrap_model_chunks_with_ddp helper added in #4623, which centralises DDP wrapping on main.

Issue tracking

Linked issue: N/A (bug fix)

Details

Symptom. Training a model with --use-precision-aware-optimizer MXFP8 wgrad + layer-wise distributed optimizer (use_layer_wise_distributed_optimizer=True) crashes inside cuBLASLt on the first wgrad call for a non-first param in any bucket:

RuntimeError: cuBLAS Error: CUBLAS_STATUS_NOT_SUPPORTED when calling cublasLtMatmulAlgoGetHeuristic

Root cause. Under the layer-wise distributed optimizer, arguments.py sets use_distributed_optimizer=False and routes through the legacy allgather_params sync path, so wrap_model_chunks_with_ddp does not supply a pre-padded full_param_layout to DDP. DDP then falls back to _compute_default_per_buffer_param_layout, which packs params back-to-back with no padding. The resulting main_grad slice for any param whose offset isn't a multiple of 64 elements (≥128 bytes for BF16/MXFP8) has only 16-byte D-pointer alignment, which cuBLASLt 12.8.x rejects for MXFP8 wgrad.

The standard distributed optimizer is unaffected because it precomputes a shard-aligned full_param_layout that already aligns per-param starts.

Fix.

  1. param_and_grad_buffer.py — add pad_param_starts: bool = False to _compute_default_per_buffer_param_layout and _ParamAndGradBuffer.__init__. When set, each param's param_start_index is rounded up to a 64-element boundary via pad_param_start (the same helper the distributed optimizer already uses). Cost: ≤ 126 bytes per param.
  2. distributed_data_parallel.py — forward the new pad_param_starts kwarg from DistributedDataParallel.__init__ down to _ParamAndGradBuffer.
  3. training.py (wrap_model_chunks_with_ddp) — when DP is DDP and use_layer_wise_distributed_optimizer=True and not use_layer_wise_param_layout (i.e. the path that supplies no full_param_layout), pass pad_param_starts=True to every wrapped chunk. The flag is ignored when a full_param_layout is supplied, so callers that already provide a layout are unaffected.

Contribution process

Pre-checks

  • I have added relevant unit tests
  • I have added relevant functional tests
  • I have added proper typing to my code
  • I have added relevant documentation (inline docstring on new args)
  • I have run the autoformatter.sh on my PR

Test plan

  • Reproduce: launch any Muon + MXFP8 wgrad training (e.g. configs/benchmarking/recipes/deepseek_v4_flash_proxy/gb200/mxfp8_*muon*.yaml) on main without this patch → CUBLAS_STATUS_NOT_SUPPORTED on iter 1.
  • Verify: same launch with this patch → training proceeds; per-iter throughput unchanged vs. Adam/standard distributed optimizer baseline.
  • Regression: --use-distributed-optimizer (standard) path is untouched (still supplies its own padded full_param_layout); BF16 layer-wise path also runs cleanly (padding is benign).

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!

🤖 Generated with Claude Code

@copy-pr-bot

copy-pr-bot Bot commented May 20, 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.

@Wohox
Wohox marked this pull request as ready for review May 20, 2026 09:51
@Wohox
Wohox requested review from a team as code owners May 20, 2026 09:51
@svcnvidia-nemo-ci
svcnvidia-nemo-ci requested a review from a team May 20, 2026 09:51
@Wohox

Wohox commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

/claude review

Comment on lines 873 to +874
for param in params[::-1]:
if pad_param_starts:

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.

Nit: The existing TestDefaultParamLayout class in tests/unit_tests/distributed/test_param_layout.py tests this function but only exercises the default (no-padding) path. Since this is a bug fix, consider adding a regression test that calls _compute_default_per_buffer_param_layout(..., pad_param_starts=True) with params whose sizes aren't multiples of 64, and asserts that every param_start_index in the resulting layout is 64-element-aligned. This would prevent the alignment invariant from silently regressing.

@claude claude Bot 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 — the fix correctly reuses the existing pad_param_start helper, the condition in training.py precisely targets the affected code path, and the parameter threading follows established codebase patterns. One minor suggestion to add a regression test for the new pad_param_starts=True path (left as an inline comment).

@Wohox

Wohox commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

Closed since already included in #4771

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.

2 participants