Conversation
The row-scaled path ran two amax kernels; the columnwise one read global memory column-major (uncoalesced) and dominated runtime. Compute both directions in one kernel that streams 128x128 chunks through shared memory via TMA and reduces columns from SMEM. Gated by fused_amax_supported (BF16, 128-aligned dims); other cases keep the two-kernel path. Set NVTE_NVFP4_FUSED_AMAX=0 to force fallback. Signed-off-by: Cael Ling <caell@nvidia.com>
for more information, see https://pre-commit.ci
Contributor
|
13 tasks
The fused wrapper zeroed both amax buffers with an unconditional memset while the kernel returns early on noop[0]==1, so a skipped (graph-replay) call cleared the previously published amax. Replace the memset with a noop-aware zero kernel that returns early on the same flag, matching the standalone kernels' contract. Signed-off-by: Cael Ling <caell@nvidia.com>
cael-ling
marked this pull request as draft
September 7, 2026 06:43
cael-ling
marked this pull request as ready for review
September 7, 2026 07:03
timmoon10
reviewed
Sep 10, 2026
Drop the NVTE_NVFP4_FUSED_AMAX switch and make fused-vs-unfused an internal detail; callers use nvfp4::row_scaled::compute_amaxes, unfused stays as fallback. Signed-off-by: Cael Ling <caell@nvidia.com>
timmoon10
reviewed
Sep 14, 2026
Comment on lines
+483
to
+490
| TRANSFORMER_ENGINE_SWITCH_CONDITION( | ||
| do_row, DO_ROW, TRANSFORMER_ENGINE_SWITCH_CONDITION(do_col, DO_COL, { | ||
| auto kernel = compute_fused_amax_kernel<DO_ROW, DO_COL>; | ||
| cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, dshmem_size); | ||
| kernel<<<grid, block, dshmem_size, stream>>>( | ||
| tensor_map_input, do_row ? row_amax_ptr : nullptr, do_col ? col_amax_ptr : nullptr, | ||
| noop_ptr, rows, cols); | ||
| })); |
Member
There was a problem hiding this comment.
Do we need to compile for the row-only or col-only cases? If the data or amax pointers are missing, it seems like we should call the unfused kernels. Also, we check amax pointers in fused_amax_supported, so will never hit the DO_ROW=false or DO_COL=false cases.
Suggested change
| TRANSFORMER_ENGINE_SWITCH_CONDITION( | |
| do_row, DO_ROW, TRANSFORMER_ENGINE_SWITCH_CONDITION(do_col, DO_COL, { | |
| auto kernel = compute_fused_amax_kernel<DO_ROW, DO_COL>; | |
| cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, dshmem_size); | |
| kernel<<<grid, block, dshmem_size, stream>>>( | |
| tensor_map_input, do_row ? row_amax_ptr : nullptr, do_col ? col_amax_ptr : nullptr, | |
| noop_ptr, rows, cols); | |
| })); | |
| auto kernel = compute_fused_amax_kernel<true, true>; | |
| cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, dshmem_size); | |
| kernel<<<grid, block, dshmem_size, stream>>>( | |
| tensor_map_input, row_amax_ptr, col_amax_ptr, noop_ptr, rows, cols); |
timmoon10
reviewed
Sep 14, 2026
Co-authored-by: Tim Moon <4406448+timmoon10@users.noreply.github.com> Signed-off-by: Tim Moon <4406448+timmoon10@users.noreply.github.com>
timmoon10
previously approved these changes
Sep 14, 2026
timmoon10
left a comment
Member
There was a problem hiding this comment.
LGTM, pending CI. I made a change to avoid unnecessary kernel compilations (#3454 (comment)), but can you sanity-check?
Member
|
/te-ci |
for more information, see https://pre-commit.ci
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The NVFP4 row-scaled path that was originally proposed in #2931 computes per-row and per-column amax with two separate kernels. This PR fuses both directions into a single kernel that streams 128x128 chunks through shared memory via TMA (coalesced loads) and does the column reduction from SMEM. Amax is an exact max reduction, so results are byte-identical to the two-kernel path. The fused path is used only when the quantize call needs both directions (rowwise + columnwise amax) on a BF16 input with 128-aligned dims; the kernel then produces both amaxes in one pass. Any other case keeps the original two kernels.
NVTE_NVFP4_FUSED_AMAX=0forces the fallback at runtime.Type of change
Changes
compute_fused_amax_kernel(rowwise + columnwise) and its host wrappersfused_amax_supported/compute_fused_amaxinquantize_transpose_nvfp4.cuh.dispatch/quantize.cuh(fwd and bwd) when supported, else fall back to the standalone amax kernels.NVTE_NVFP4_FUSED_AMAXkill switch (default on).Performance
Full-quantize median latency, fused (fused amax + cast) vs (row-wise & columnwise amax + cast), the cast kernel is byte-identical so the delta is the amax step:
Reproduce
Single Blackwell (SM100) GPU.
pytest tests/pytorch/nvfp4/test_nvfp4_quantize_exact.py -k "row_scaled and both_directions"passes; rowwise/columnwise amax and
qx/qx_tmatch the reference exactly.NVTE_NVFP4_FUSED_AMAXenv var (default enabled): set to0to disable the fused path at runtime and fall back to the two standalone amax kernels, no rebuild needed.Checklist: