Numerical accuracy issue with Triton MXFP4 kernel with respect to quantization
I maintainer di solito rispondono entro 2 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 55/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Tranquilla
- Stack tecnologico
- python
- Ambito
- machine-learning, performance
Direzione di ricerca
Start in transformer_engine/pytorch/triton_kernels/cast_transpose.py, reading te_cast_transpose_mxfp4_triton and _cast_transpose_triton_mxfp4 alongside the attached reproduction script. Compare Triton and native outputs with shuffle_columnwise_data enabled and disabled. Done means the Triton columnwise FP4 data changes with the flag and matches the native preshuffled layout, while the existing scale-swizzle behavior remains correct.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
The Triton MXFP4 cast-transpose path (te_cast_transpose_mxfp4_triton, selected by
NVTE_USE_CAST_TRANSPOSE_TRITON=1) does not honor the quantizer's shuffle_columnwise_data /
shuffle_rowwise_data flags. It applies the scale swizzle (with_gemm_swizzled_scales) but
stores the packed FP4 data in the plain transposed layout, ignoring the data-preshuffle flags
entirely.
The native/HIP path (tex.quantize → te_mxfp4::cast_transpose_mxfp4_shuffled) does apply the data
preshuffle. So when a recipe sets shuffle_columnwise_data=True (the real MXFP4 training layout),
the Triton kernel's columnwise operand is unshuffled and does not match the preshuffled layout
the AITER a4w4 GEMM consumes (bpreshuffle=True).
Because the columnwise operand feeds the backward weight-gradient (wgrad) GEMM, the FP4 values
are correct but their ordering is wrong. This produces structurally-wrong weight gradients that
compound over many steps into a slow grad-norm blow-up (not an immediate NaN).
Reproduction
Attaching a script which helps in reproduction of bug. triton_mxfp4_ignores_columnwise_shuffle.py
The attached script reproduces the bug on a single GPU (random bf16 tensor, no training). It
quantizes the same tensor with both kernels and compares the columnwise FP4 data with the shuffle
flag on and off: the native output changes when the flag flips (it preshuffles), while the Triton
output stays byte-identical (it ignores the flag).
Expected vs. actual
- Expected: with
shuffle_columnwise_data=True, the Triton kernel's columnwise FP4 data is
preshuffled identically to the native kernel. - Actual: the Triton kernel emits identical columnwise data whether the flag is True or False —
i.e. it ignores the flag and leaves the data unshuffled.
Environment
- GPU: AMD Instinct MI355X (gfx950), ROCm 7.x
- Image (reproduces the bug):
rocm/primus:pytorch-2.12.0-rocm7.14.0a20260608_te-2.14.0.dev0-e6ede467_v26.4
Root cause (code pointers)
In transformer_engine/pytorch/triton_kernels/cast_transpose.py, the launcher only ever reads the
scale-swizzle flag; the data-shuffle flags are never consulted:
# te_cast_transpose_mxfp4_triton
shuffle_scales = out_metadata["quantizer"].with_gemm_swizzled_scales
SHUFFLE_ROWWISE_SCALING = shuffle_scales and USE_ROWWISE_SCALING
SHUFFLE_COLWISE_SCALING = shuffle_scales and USE_COLWISE_SCALING
# quantizer.shuffle_columnwise_data / shuffle_rowwise_data are never read.
Inside the kernel (_cast_transpose_triton_mxfp4), the columnwise branch stores FP4 data with a
plain transposed store and then swizzles only the scales:
if USE_COLWISE:
packed_col, bs_col = _mxfp4_quantize_32x32_block(tl.trans(x_chunk), MXFP4_BLOCK_SIZE)
tl.store(colwise_fp4_ptr + ..., packed_col, mask=col_fp4_mask) # plain transpose, no data preshuffle
if SHUFFLE_COLWISE:
... # shuffles SCALES only
The native C++/HIP path reads the same flags and applies the data preshuffle in-kernel, so the two
kernels diverge only when a data-shuffle flag is set.
Impact
With NVTE_USE_CAST_TRANSPOSE_TRITON=1 and a recipe that sets shuffle_columnwise_data=True, wgrad
reads a misordered FP4 operand. In a Flux-12B MXFP4 run (SR off) this turns a converging run
(loss ~0.63, grad norm ~0.2) into a diverging one (grad norm climbing to ~58 over ~1000 iters),
while the native kernel converges under identical settings.
Additional: the Triton path is also slower
Independently of the correctness bug above, the Triton cast-transpose is structurally slower than the
native kernel, for these reasons:
- Two quantizes plus an in-kernel transpose per tile. The columnwise operand is produced by
physically transposing each tile (tl.trans) and re-running the full quantizer, reusing none of
the rowwise work. The native kernel emits both the rowwise and columnwise layouts in a single
fused pass. - Expensive per-element encoding. The E8M0 scale is computed with transcendentals
(log2/floor/exp2) instead of reading the exponent bits directly, and the E2M1 data uses a
dependent chain oftl.wherecomparisons instead of the gfx950 hardware packed-FP4 cast. - Not autotuned. The kernel uses a fixed tile size and default launch parameters with generic
codegen, so it under-uses the GPU and cannot adapt to different tensor shapes. The native kernel
is hand-tuned for gfx950.
- Lingua principale
- Python
- Stelle
- 77
- Fork
- 36
- Merge medio
- 3g 16h
- PR unite (30g)
- 10
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di ROCm/TransformerEngine
-
CP LSE merge switches compiled variants on ROCm, breaking forward reproducibilityForse già presa @zjin-lcf l’ha presa 54 giorni fa. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
ROCm/TransformerEngine#693 ·
I maintainer di solito rispondono entro 2 giorni
-
MXFP8 uses logical block size 32, but some ROCm training paths still hard-require `block_len == 128`Forse di nuovo libera @adityas-amd l’ha presa 182 giorni fa e non c’è nessuna pull request aperta. Apertabug status: triage
ROCm/TransformerEngine#539 · 1 commento · 1 assegnatario ·
I maintainer di solito rispondono entro 2 giorni
-
Enable FP8 (E4M3/E5M2) kernel execution for Radeon AI PRO R9700 (gfx1201, RDNA4)Forse di nuovo libera @adityas-amd l’ha presa 334 giorni fa e non c’è nessuna pull request aperta. Apertaenhancement status: triage
ROCm/TransformerEngine#359 · 6 commenti · 2 reazioni · 1 assegnatario ·
I maintainer di solito rispondono entro 2 giorni
-
nvfp4Apertaenhancement
Difficoltà 4/5 3-5 giorni Idoneità per principianti 30/100
ROCm/TransformerEngine#358 ·
I maintainer di solito rispondono entro 2 giorni
-
Under Investigation
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
ROCm/TransformerEngine#82 · 14 commenti · 1 reazione ·
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di ROCm/TransformerEngine
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 62/100
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 70/100
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
topoteretes/cognee#5773 ·
I maintainer di solito rispondono entro 1 giorno
-
MiKTeX: a failed build reports no errors because texify --quiet switches the log to file:line: styleAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
SublimeText/LaTeXTools#1718 ·