Skip to content

NVFP4 native weights for DDP - #4005

Merged
Phlip79 merged 18 commits into
NVIDIA:mainfrom
WanZzzzzz:fp4_native_weights
Apr 14, 2026
Merged

Phlip79 merged 18 commits into
NVIDIA:mainfrom
WanZzzzzz:fp4_native_weights

Conversation

@WanZzzzzz

@WanZzzzzz WanZzzzzz commented Mar 23, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do ?

Add support for NVFP4 native (packed) weights in Megatron's Distributed Data Parallel (DDP) pipeline. This allows parameters to be stored, communicated, and gathered in NVFP4 format, reducing memory footprint and communication volume during training.

Problem

Previously, NVFP4 quantization was only applied at the compute level via Transformer Engine. The DDP param buffers and all-gather operations still used full-precision (BF16) representations, missing the memory and bandwidth savings that native NVFP4 storage provides — similar to what FP8 native weights already support.

Solution

Extend the existing FP8 native weight infrastructure to support NVFP4, accounting for NVFP4's unique dual-buffer layout: the param buffer stores packed bytes (half numel) while the grad buffer uses full numel.
Important notice: This PR only support 2D quantized nvfp4 weights. With 2D quantization, rowwise and colwise tensors share the same values. Given this, we only need to all-gather the rowwise data and transpose to get colwise data later.

Key Changes

Core DDP infrastructure (param_and_grad_buffer.py)

  • NVFP4 params use packed storage (numel // 2) in the param buffer but full numel in the grad buffer
  • Added nvfp4_grad_index_map for separate grad buffer offset tracking
  • Added get_grad_index_map() so the distributed optimizer uses correct (full-numel) indices for grad/param range calculations
  • Bucket creation passes separate grad offsets for NVFP4 buffers
  • partition_buckets updated to support reduce_scatter_with_fp32_accumulation (one bucket per group)

Distributed optimizer (distrib_optimizer.py)

  • Extended FP8 model param / main param initialization paths to also handle NVFP4
  • Added _get_nvfp4_params_and_shard_fp32_from_nvfp4() to collect NVFP4 params with their FP32 master weight shards
  • _copy_main_params_to_model_params calls quantize_nvfp4_param_shard to cast FP32 shards back to NVFP4 after optimizer step

FP4 utilities (fp4_utils.py)

  • get_nvfp4_rowwise_packed_shape: compute packed byte shape for param buffer
  • modify_nvfp4_rowwise_storage: swap NVFP4 tensor's rowwise data pointer into the DDP param buffer
  • quantize_nvfp4_param_shard: wraps TE's quantize_master_weights for shard-level FP32→NVFP4 casting
  • fp4_model_init now passes preserve_high_precision_init_val=True during training to retain BF16 init values for accurate FP32 main param initialization

Arguments & config

  • Added --fp4-param-gather CLI flag (consistent with existing --fp8-param-gather)
  • Added fp4_param_gather to DistributedDataParallelConfig
  • Fixed TransformerConfig.fp4_param field definition to be consistent with fp8_param
  • Added fp4_param to ArgumentGroupFactory exclude list to prevent duplicate argparse registration

Tests (test_fp4_param.py)

  • Training correctness: verified NVFP4 vs BF16 loss bitwise match (with/without DP overlap)
  • Inference mode
  • First/last layers in BF16
  • Determinism helper for debugging

Temporary aids for deterministic-mode debugging (to be removed before merge)

  • Stochastic rounding disabled in NVFP4 recipe (disable_stochastic_rounding=True)
  • grad_norm rounding in ChainedOptimizer (round(grad_norm, 4))

⚠️ For major changes (either in lines of code or in its impact), please make sure to first share a design doc with the team. If you're unsure what's the best way to do so, contact the @mcore-oncall.

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.

Signed-off-by: qiyuw <qiyuw@nvidia.com>
@WanZzzzzz
WanZzzzzz requested review from a team as code owners March 23, 2026 20:44
@copy-pr-bot

copy-pr-bot Bot commented Mar 23, 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 March 23, 2026 20:44
@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.

Signed-off-by: qiyuw <qiyuw@nvidia.com>
Signed-off-by: qiyuw <qiyuw@nvidia.com>
Signed-off-by: qiyuw <qiyuw@nvidia.com>
Signed-off-by: qiyuw <qiyuw@nvidia.com>
@WanZzzzzz
WanZzzzzz marked this pull request as ready for review March 23, 2026 22:49
@svcnvidia-nemo-ci
svcnvidia-nemo-ci requested a review from a team March 23, 2026 22:49
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the Final Review PR is in the "final review" stage label Mar 23, 2026
Signed-off-by: qiyuw <qiyuw@nvidia.com>
@erhoo82 erhoo82 added the 26.04 label Mar 23, 2026
Comment thread megatron/training/arguments.py Resolved
@gautham-kollu

Copy link
Copy Markdown
Contributor

Thanks for the detailed description

Signed-off-by: qiyuw <qiyuw@nvidia.com>
@Phlip79
Phlip79 requested a review from gautham-kollu March 25, 2026 05:23
@Phlip79

Phlip79 commented Mar 25, 2026

Copy link
Copy Markdown
Member

/claude review

Comment thread megatron/training/arguments.py Resolved
Comment thread megatron/core/optimizer/optimizer.py Resolved
@Phlip79

Phlip79 commented Mar 25, 2026

Copy link
Copy Markdown
Member

/ok to test fc717b2

Comment thread megatron/core/fp4_utils.py Resolved
Signed-off-by: qiyuw <qiyuw@nvidia.com>
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the Approved All necessary approvals have been made label Apr 13, 2026
@WanZzzzzz

Copy link
Copy Markdown
Contributor Author

/ok to test 51ad2bf

@Phlip79

Phlip79 commented Apr 13, 2026

Copy link
Copy Markdown
Member

The label "Approved" is being added by mistake due to a bug. This will be fixed in #4280.

# Grad buffer: [g0, g1, g2, g3, ...] numel = N
#
# We therefore maintain two index maps:
# - param_index_map: offsets into the packed param buffer (numel // 2)

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.

Thinking about this more, I'm wondering if we should name the two variables param_index_map and packed_param_index_map. param_index_map has the vanilla mapping, and packed_param_index_map has the offsets in the packed param buffer.

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.

Do we have to make this so general? Packing two values into one byte is specific to nvfp4 case, not fp6 or fp2 even if we have it in the future. If we do the renaming, the current param_index_map will become packed_param_index_map and nvfp4_grad_index_map will become param_index_map, which requires a big refactoring of this PR.

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.

Well, the other way to think about this is we are keeping param_index_map un-changed from before. And adding a new packed_param_index_map or nvfp4_packed_param_index_map (or something else) to handle the specific NVFP4 case.

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.

Basically, I don't like the concept of a grad_index_map. Everything is just a param_index_map. And if a parameter takes less than the smallest data type we have available, then we have to do something special (and that's the special case).

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.

I renamed nvfp4_grad_index_map to nvfp4_unpacked_param_index_map, which serves the purpose here and avoids changing the logic.

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.

Sorry, I don't love this...you are changing what param_index_map means when NVFP4 is used. nvfp4_unpacked_param_index_map now does what param_index_map does for BF16 or MXFP8 training, and param_index_map stores different things based on the precision.

Signed-off-by: qiyuw <qiyuw@nvidia.com>
@WanZzzzzz

Copy link
Copy Markdown
Contributor Author

/ok to test 4cebfe8

Signed-off-by: qiyuw <qiyuw@nvidia.com>
@WanZzzzzz

Copy link
Copy Markdown
Contributor Author

/ok to test 2b97a34

#
self.has_nvfp4_params = any(is_nvfp4tensor(p) for p in self.params)
self.nvfp4_unpacked_param_index_map = {}
grad_start_index = 0 if self.has_nvfp4_params else None

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.

There is no grad_index_map now. Update these variables to mirror the name of the index map (self.nvfp4_unpacked_param_index_map).

@WanZzzzzz WanZzzzzz Apr 14, 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.

grad_start_index and grad_end_index are used to get the index of grad data in a grad bucket. I don't think we should rename them to unpacked_param_start_index or something.

@deepakn94
deepakn94 removed the request for review from a team April 14, 2026 01:15
@Phlip79

Phlip79 commented Apr 14, 2026

Copy link
Copy Markdown
Member

This PR is actually ready to merge now (i.e. the "Approved" label is accurate).

@deepakn94
deepakn94 added this pull request to the merge queue Apr 14, 2026
@svcnvidia-nemo-ci

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started!

You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/24375815484

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 14, 2026
@Phlip79
Phlip79 added this pull request to the merge queue Apr 14, 2026
@svcnvidia-nemo-ci

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started!

You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/24381067948

Merged via the queue into NVIDIA:main with commit e1db4a0 Apr 14, 2026
66 of 67 checks passed
ko3n1g pushed a commit that referenced this pull request Apr 14, 2026
Signed-off-by: qiyuw <qiyuw@nvidia.com>
Signed-off-by: NeMo Bot <nemo-bot@nvidia.com>
yangbofun pushed a commit to xlm-research/Megatron-LM that referenced this pull request May 22, 2026
Signed-off-by: qiyuw <qiyuw@nvidia.com>
yhgalaxy pushed a commit to yhgalaxy/Megatron-LM that referenced this pull request Jun 17, 2026
Signed-off-by: qiyuw <qiyuw@nvidia.com>
Signed-off-by: yhgalaxy <yhgalaxy@outlook.com>
jon-barker pushed a commit to jon-barker/Megatron-LM that referenced this pull request Jul 10, 2026
Signed-off-by: qiyuw <qiyuw@nvidia.com>
Signed-off-by: Jon Barker <jbarker@aws-cmh-slurm-1-vscode-02.cm.cluster>
terminator123 pushed a commit to 021ai/Megatron-LM that referenced this pull request Aug 3, 2026
Signed-off-by: qiyuw <qiyuw@nvidia.com>

This branch was previously deployed

1 inactive deployment
test — 2b97a340 Deployed Apr 13, 2026 by copy-pr-bot[bot] via cicd-wait-in-queue #11398
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

26.04 26.04.01 Approved All necessary approvals have been made complexity: high core_r0.17.0 Auto-cherrypick to release branch. Apply before merge; cherrypick happens after merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants