Mask a_s/b_s scale loads in blockwise FP8 training GEMM kernels - #4793
Open
amanyagami wants to merge 1 commit into
Open
Mask a_s/b_s scale loads in blockwise FP8 training GEMM kernels#4793amanyagami wants to merge 1 commit into
amanyagami wants to merge 1 commit into
Conversation
Both triton_fp8_gemm_1x128_128x128_kernel and triton_fp8_gemm_1x128_128x1_kernel in torchao/prototype/blockwise_fp8_training/kernels.py load their per-tile a_s/b_s scale values without a bounds mask, while every other load and the final store in these kernels is masked. When the autotuner's tile does not evenly divide M or N, the scale loads for the tail tile dereference memory past the end of a_s/b_s. The GEMM's numeric output is unaffected (the out-of-range lanes are dropped by c_mask at the store), so this is a memory-safety bug, not a numerical one - reported via compute-sanitizer as an 'Invalid __global__ read' that intermittently surfaces as 'CUDA error: unspecified launch failure' depending on allocator slack. Fix: mask=offs_m < M for the a_s load, mask=offs_n < N for the b_s load, matching the bounds every sibling load (a_mask, b_mask) and the final store (c_mask) already use in the same kernels. These kernels require CUDA to run, so they can't be exercised in this environment. Added test/prototype/blockwise_fp8_training/test_gemm_scale_bounds.py, which isolates the offs_m/offs_n -> scale-index arithmetic in plain Python for tile geometries where M/N are not a multiple of the block size (exactly the case the issue reports) and checks that unmasked indices go out of [0, M)/[0, N), while masked indices never do and still cover every valid row/col. This proves the fix's indices are correct independent of running the actual Triton kernel; end-to-end verification (e.g. under compute-sanitizer) still needs GPU CI. Fixes pytorch#4634
amanyagami
requested review from
andrewor14,
jerryzh168 and
vkuzo
as code owners
August 19, 2026 20:34
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/ao/4793
Note: Links to docs will display an error until the docs builds have been completed. This comment was automatically generated by Dr. CI and updates every 15 minutes. |
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.
Summary
Both
triton_fp8_gemm_1x128_128x128_kernelandtriton_fp8_gemm_1x128_128x1_kernelintorchao/prototype/blockwise_fp8_training/kernels.pyload their per-tilea_s/b_sscale values without a bounds mask, while every other load and the final store in these kernels is masked. When the autotuner's tile does not evenly divideMorN, the scale loads for the tail tile dereference memory past the end ofa_s/b_s.The GEMM's numeric output is unaffected — the out-of-range lanes are dropped by
c_maskat the store — so this is a memory-safety bug, not a numerical one. The issue reports it via compute-sanitizer as anInvalid __global__ read, which intermittently surfaces asCUDA error: unspecified launch failuredepending on allocator slack.Fix
mask=offs_m < Mfor thea_sload,mask=offs_n < Nfor theb_sload — matching the bounds every sibling load (a_mask,b_mask) and the final store (c_mask) already use in the same kernels.Test
These kernels require CUDA to run (real
@triton.jitGEMMs targeting H100/B200), so I can't exercise them in this environment. Instead, addedtest/prototype/blockwise_fp8_training/test_gemm_scale_bounds.py, which isolates the exactoffs_m/offs_n→ scale-index arithmetic in plain Python for tile geometries whereM/Nare not a multiple of the block size (the exact case the issue reports), and checks:[0, M)/[0, N)for these shapes (reproducing the bug's precondition)mask=offs_m < M/mask=offs_n < N) never do, and still cover every valid row/col at least onceThis proves the fix's indices are correct independent of actually running the Triton kernel. End-to-end verification (e.g. under
compute-sanitizer, per the issue's own repro) still needs GPU CI — flagging this explicitly.Fixes #4634
🤖 Generated with Claude Code