Repository navigation
Support row-only MXFP8 distributed master-weight casts - #3488
Conversation
Signed-off-by: xiuhu17 <zhihao.wang@perplexity.ai>
for more information, see https://pre-commit.ci
Signed-off-by: xiuhu17 <zhihao.wang@perplexity.ai>
[Medium impact] The PR appears safe to merge; no actionable new issues were found. SummarySupports distributed master-weight updates for row-only MXFP8 weights while keeping the bidirectional path.
The previous, unnumbered CI-coverage finding is addressed: the existing L1 suite launches two ranks and runs the row-only, mixed-layout, fragment-output, and empty-shard cases. Review checks were source-only; no GPU tests were run. Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Master-weight shards] --> B[Compute amax for present directions]
B --> C[One packed MAX all-reduce]
C --> D[Update scales for present directions]
D --> E[Write rowwise output]
D --> F{Columnwise output present?}
F -->|Yes| G[Require scales and write columnwise output]
F -->|No| H[Skip columnwise reads and writes]
Reviews (5) · Last reviewed commit: "Select MXFP8 partial-cast directions fro..." · Reviewed by Greptile |
| * \param[in] input Input tensor (continuous segment of flattened original tensor). | ||
| * \param[in,out] amax_rowwise Output tensor for row-wise maximum absolute values. | ||
| * \param[in,out] amax_colwise Output tensor for column-wise maximum absolute values. | ||
| * Shape [0, 0] skips column-wise computation. |
There was a problem hiding this comment.
Checking for empty 2D shape is not great. Just check for has_data being false (empty NVTETensor).
| * original tensor. To skip column-wise computation, pass output_colwise with shape [0] | ||
| * and scale_inv_colwise with shape [0, 0]. Row-wise outputs remain required. |
| "Wrong padding of amax_colwise's cols"); | ||
| NVTE_CHECK(amax_colwise.data.shape[1] >= cols, "Invalid cols"); | ||
| NVTE_CHECK(amax_colwise.dtype() == input.dtype(), "Wrong dtype of amax_colwise"); | ||
| // Empty views can have a non-null data pointer: use shape to detect omission. |
There was a problem hiding this comment.
If there is an issue of the pointer passed from the PyTorch side, then that should be handled in the PyTorch extension rather than WARing it here.
|
|
||
| // Load scales_colwise | ||
| { | ||
| if (output_colwise != nullptr) { |
There was a problem hiding this comment.
Please include some benchmark results on the old vs new time for the case still using columnwise output to see whether those additional conditions didn't introduce overhead. If they did then they would need to be handled via templated kernels.
There was a problem hiding this comment.
Fixed the amax regression with boolean templates in 704b170; benchmark results are in the PR description.
| # Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # See LICENSE for license information. | ||
|
|
||
| """Run with pytest on one GPU or torchrun -m pytest on multiple GPUs.""" |
There was a problem hiding this comment.
Why do we need a separate test file for that? It should be part of one of the larger suites like test_sanity or test_numerics. Also, for the separate test files, they would not actually be run unless they are part of the test.sh in one of the directories in qa/.
There was a problem hiding this comment.
Moved the tests into the existing L0 and L1 suites in 704b170.
Signed-off-by: Zhihao Wang <zhihao.wang@perplexity.ai>
Use columnwise output presence to select partial-cast computation in the common API and PyTorch binding. Require scales for requested columnwise output and ignore them when that output is omitted. Document the contract and cover unused scales, empty shards, and missing required scales in regression tests. Signed-off-by: Codex <codex@openai.com>
|
/te-ci |
|
Hi @ptrendx, seems ci is passed. Would you like help to merge? Thanks! |

Description
Support distributed master-weight writeback into MXFP8 primary weights whose columnwise data and scales are absent, while preserving the bidirectional path.
Memory motivation: radixark/miles#615
Related initialization change: #3468
Related Megatron integration: NVIDIA/Megatron-LM#7095
Changes
NVTETensorhandles (has_data() == false). The PyTorch extension normalizes zero-element tensors, including views with backing storage, into that native representation. Common code no longer depends on a magic empty 2D shape.test_partial_cast.py(L0), distributed updates indistributed/test_cast_master_weights_to_fp8.py(L1). The existing two-rank launch now covers mixed layouts, fragment outputs, a boundary inside a 32-value block, and an empty master shard on a participating rank.Scope
MXFP8 distributed cast only. No initialization policy, TMS/backup, NVFP4, or Megatron fallback changes. This is not complete FSDP or full-training integration coverage. Megatron's separate gather copy-back layout handling remains relevant.
Validation of the revised implementation
Source-built TE on NVIDIA GB200: CUDA compiler 13.3, Torch 2.14.0+cu130, Python 3.14.6. Python/native import paths point to the isolated new build. The benchmark library and installed test library have the same ELF build ID.
test_partial_cast.pyplusmxfp8/test_mxfp8_master_weight_empty_shard.py: 5 passed.distributed/test_cast_master_weights_to_fp8.py, including its real two-rank subprocess: 4 passed.git diff --check: pass.The environment emits Python 3.14 TorchScript/opaque-type deprecation warnings. Full Megatron training, checkpoint/resharding and end-to-end FSDP are not covered here.
Bidirectional kernel performance
The initial runtime-pointer-guard implementation did regress amax by 21–35% in this matrix. Following the review suggestion, both kernels now use compile-time boolean specialization.
Baseline: unmodified kernel source at merge base
5f6105b9; revised source:704b1707. Both are built with the same CUDA 13.3 compiler,-O3, and GB200sm_100atarget. Both columnwise and rowwise outputs are enabled in every measurement. Outputs are checked bitwise before timing.Method: CUDA-event timing of CUDA-graph replays, seven alternating old/new rounds; median time per kernel call, excluding Python dispatch and allocation. Each graph captures 50 calls for input sizes up to 4M elements, otherwise 10 calls, with 20 replays per measured round. Four shapes × BF16/FP32 × full/partial input × two kernels = 32 comparisons. Partial inputs start at offset 17 and contain one quarter of the full matrix.
Full-input results, microseconds per call:
Across all 32 full/partial comparisons, revised/baseline time is 0.994–1.020×. The large amax regression is eliminated; these microbenchmarks do not establish an end-to-end training speedup. The standalone benchmark harness is not added to repository runtime or CI code.
Size and checklist
This review-fix commit changes six files, +150 / -169 lines (net -19), including removing the separate test file and reusing the existing suites.