[BUG] Grouped MXFP8 activation kernels return all-zero output when tensor widths differ (CUDA 12.x builds)
メンテナーはふだん 2 日以内に返信
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 35/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 停滞
- 技術スタック
- cpp
- 領域
- performance
調査の方向性
Read transformer_engine/common/cast/core/grouped_tma.cuh and inspect the linkage of g_tensor_maps and update_tma_descriptors; the issue links PR #3704, so check its status before starting. Reproduce with the stated *GroupedFusedCastMXFP8* test filter on a CUDA 12.8 Blackwell build. Done means grouped activation outputs and scales match the CPU reference for varying-width shapes.
索引モデルが issue の本文から書いたものです。
説明
Describe the bug
When Transformer Engine is built with CUDA 12.x, the grouped MXFP8 activation functions (nvte_group_gelu,
nvte_group_dgelu, and the other grouped activations) return an output that is entirely zero, with no CUDA
error, when the tensors in the group have different widths (VARYING_LAST_DIM or VARYING_BOTH_DIMS). Plain grouped
quantization (nvte_group_quantize) gives correct results for the same inputs.
Cause. For groups with different widths, two kernels work together:
update_tma_descriptorswrites one TMA descriptor plus rows/cols/offset per tensor into a GPU table,
g_tensor_maps.group_quantize_mxfp8_kernelreads that table and does the quantization.
In transformer_engine/common/cast/core/grouped_tma.cuh the table and the writer kernel have different linkage:
// Internal linkage avoids device-link ODR issues when this header is included by multiple .cu TUs.
static __device__ TensorMapStorage g_tensor_maps; // one private copy per .cu file
template <typename IType, typename OType>
__global__ void __launch_bounds__(THREADS_PER_WARP)
update_tma_descriptors(...) // externally linked template
- Because of
static, every.cufile that includes the header gets its own copy of the table. Since #2983 split
the grouped activations into separate files, 17 files do this (cast/cast_grouped*.cuand
activation/*_grouped*.cu). - With CUDA 12.x, nvcc gives template kernel launchers (host stubs) external linkage, so the 17 identical
instantiations are merged at link time. The builtlibtransformer_engine.sohas one launcher for
update_tma_descriptors<__nv_bfloat16, __nv_fp8_e4m3>(nm -C), so every launch runs one file's kernel and
writes that file's table. - The activation kernels are compiled only in their own files (e.g.
gelu_grouped.cu) and read their own copy,
which nothing ever wrote. They seerows == 0, return early, and leave the output untouched.
Why it depends on the CUDA version. From CUDA 13.0, nvcc's -static-global-template-stub defaults to true,
which gives template kernel launchers internal linkage in whole-program mode; before 13.0 it defaults to false
(NVIDIA blog, which
describes this exact failure mode). TE builds without -rdc and doesn't set the flag. Measured on the same commit:
| Build | Launcher copies in .so |
*GroupedFusedCastMXFP8* |
|---|---|---|
| CUDA 12.8, unchanged | 1 | 240 failed, 1,055 passed |
CUDA 12.8 + -static-global-template-stub=true |
17 | 0 failed, 1,295 passed |
| CUDA 12.8 + proposed fix (below) | 17 | 0 failed, 1,295 passed |
| CUDA 13.0, unchanged | 17 | not run (driver on that machine only supports 12.8) |
CUDA 13.0 + -static-global-template-stub=false |
1 | not run |
This is probably why CI doesn't show it. In a 12.x build, which file's launcher survives depends on link order, which
is presumably why plain cast happens to work. A change elsewhere (adding or reordering .cu files, a different
linker) could move the problem to nvte_group_quantize, which PyTorch and JAX use.
Steps/Code to reproduce bug
Build the common library and the C++ tests with CUDA 12.8 for a Blackwell GPU, then run:
tests/cpp/build/operator/test_operator --gtest_filter='*GroupedFusedCastMXFP8*'
Note: on current main, tests/cpp doesn't compile with CUDA < 13.4 (separate problem, see Additional context).
I built the tests with a one-line local workaround in tests/cpp/test_common.h (if → if constexpr in
TypeInfo::Helper::getType); the library itself was unmodified.
Result at 33f3b356 (RTX 5090, CUDA 12.8): 240 failed, 1,055 passed, 1,044 skipped. The failures are exactly
{CAST_ACT, CAST_DACT} × {GeLU, SiLU, ReLU, QGeLU, SReLU} × {VARYING_LAST_DIM (9 cases), VARYING_BOTH_DIMS (15 cases)}, across input types (FP32/FP16/BF16), output types (E4M3/E5M2) and scaling directions.
SAME_BOTH_DIMS, VARYING_FIRST_DIM and CAST_ONLY all pass. Example:
CAST_ACTXGeLU_ROWWISE_VARYING_LAST_DIM_N_3_SHAPE_256X896_bfloat16_float8e4m3
Error in rowwise_scales: Mismatch at (0): 0 vs 118
33 mismatche(s) which is more than tolerable mismatch limit of 32.
The test reports only one bad scale because its compare helpers stop counting after the first mismatch. With
temporary instrumentation in the test (earlier run, 5759fa0f), all 229,376 output values and every scale are 0.
The failure is deterministic (3/3 runs; running the op 2–3 times in a row doesn't change it).
compute-sanitizer memcheck, initcheck and synccheck report no errors.
Expected behavior
Grouped activation + quantize should produce the same result as the CPU reference for every shape mode and every
supported CUDA version, as nvte_group_quantize already does.
Proposed fix
Give the updater the same internal linkage as the table, so each file uses its own updater with its own table:
template <typename IType, typename OType>
-__global__ void __launch_bounds__(THREADS_PER_WARP)
+static __global__ void __launch_bounds__(THREADS_PER_WARP)
update_tma_descriptors(const __grid_constant__ CUtensorMap base_tensor_map_input,
On CUDA 12.8 this gives 17 launcher copies and 0 failures in *GroupedFusedCastMXFP8* and in the wider
*Grouped*:*MXFP8*:*mxfp8*:*Group* sweep. It works on every supported CUDA version, and on CUDA 13 it matches what
the compiler already does by default. An alternative is building with -static-global-template-stub=true when
CUDA >= 12.8, which would also cover any other template kernels with the same pattern, but the flag doesn't exist
before 12.8 (TE supports 12.1+).
I'm happy to open a PR with this.
Environment overview
- Environment location: Cloud (RunPod), Docker container
- Method of install: from source. Common library only, configured directly with CMake:
cmake -GNinja -S transformer_engine/common -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_CUDA_ARCHITECTURES=120 -DCUDNN_FRONTEND_INCLUDE_DIR=<nvidia-cudnn-frontend>/include -DNVTE_WITH_NCCL_EP=OFF -DNVTE_WITH_CUTEDSL=OFF;
C++ tests withcmake -GNinja -S tests/cpp -B tests/cpp/build -DCMAKE_CUDA_ARCHITECTURES=120 -DTE_LIB_PATH=build
Environment details
- OS: Ubuntu 24.04.5, gcc 13.3.0
- Transformer Engine:
mainat33f3b356 - CUDA: 12.8 (nvcc 12.8, driver 570.195.03); CUDA 13.0 nvcc for the compile-only rows
- cuDNN: 9.27 (pip,
nvidia-cudnn-cu12/-cu13), cudnn-frontend 1.31.0 - PyTorch/Python: not used (C++ tests only)
Device details
- GPU: NVIDIA GeForce RTX 5090 (compute capability 12.0)
Additional context
- The GPU model shouldn't matter: any Blackwell GPU (these tests skip below compute capability 10.0) running a
CUDA 12.x build of TE should fail the same way. I haven't run it on B200. - #3630 (@kainzhong) is a different problem with the same
g_tensor_maps: concurrent launches on different streams
share one table. This issue is the opposite: the writer and reader use different copies. If #3630 is fixed by moving
the descriptors out of thestatic __device__global (e.g. into per-call storage), that would fix this too. The
one-line change above is independent and could go in first. group_dequantize_mxfp8.cuhhas a similar structure (per-file__device__tables plus an externally linked updater
template), but only one file calls it, so it isn't affected today.- Separate problem found while reproducing: since #3325,
tests/cppfails to compile with CUDA < 13.4
(static assertion failed with "tuple index must be in range"intest_common.h).TypeInfo::typesonly includes
fp8ue5m3whenCUDA_VERSION >= 13040, butDType::kFloat8UE5M3always exists, and the non-constexprifin
Helper::getType()instantiatestuple_element<11>. I can file this separately.
- 主要言語
- Python
- スター
- 3.6k
- フォーク
- 862
- 平均マージ
- 4日 15時間
- マージ済み PR(30日)
- 58
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
NVIDIA/TransformerEngine のほかの issue
-
[Bug] group_quantize_fp8_blockwise: mbarrier invalidated before other threads finish waiting on itオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
NVIDIA/TransformerEngine#3647 ·
メンテナーはふだん 2 日以内に返信
-
[PyTorch] fp8_cs_quantize fake implementation returns a vector inverse scale instead of a scalar再び着手できるかも @sanjana658 が 10 日前に担当しましたが、オープン中のプルリクエストはありません。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
NVIDIA/TransformerEngine#3636 · コメント 2 件 ·
メンテナーはふだん 2 日以内に返信
-
[Bug] Backend selection picks FA3 for training with head_dim_qk=192 / v_head_dim=128, but FA3 backward cannot run it再び着手できるかも @modelpath-dev が 44 日前に担当しましたが、オープン中のプルリクエストはありません。 オープンattention
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
NVIDIA/TransformerEngine#3481 · コメント 4 件 ·
メンテナーはふだん 2 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
NVIDIA/TransformerEngine#2189 · コメント 7 件 · リアクション 5 件 ·
メンテナーはふだん 2 日以内に返信
-
bug
難易度 4/5 3〜5日 初心者へのやさしさ 30/100
NVIDIA/TransformerEngine#3689 · コメント 1 件 ·
メンテナーはふだん 2 日以内に返信
NVIDIA/TransformerEngine の issue をすべて見る
似ている issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 65/100
521xueweihan/HelloGitHub#4025 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
aws-samples/sample-per-model-bedrock#321 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1〜3時間 初心者へのやさしさ 68/100
FavioVazquez/showtime#101 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
koush/ha_scrypted#81 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
google/osv-scanner-action#147 ·