float8: work around Triton fp8 store miscompile in compiled training - #4652
Conversation
|
Stack from ghstack (oldest at bottom): |
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/ao/4652
Note: Links to docs will display an error until the docs builds have been completed. ❌ 1 New Failure, 1 PendingAs of commit c96f599 with merge base 22705bd ( NEW FAILURE - The following job has failed:
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
|
@claude review |
|
Claude finished @andrewor14's task in 2m 2s —— View job Code Review
Overall this is a well-reasoned, well-documented workaround for a nasty, flaky, silent-NaN bug. The root-cause writeup in the module docstring is excellent and the defensive Substantive1. The blast radius is process-global, not float8-scoped — and the comments imply otherwise. 2. Silent no-op on internal drift reintroduces a silent-NaN bug. 3. The 4. Interaction with inductor's compile cache. The monkeypatch changes codegen output but is invisible to inductor's FXGraph/on-disk cache key. A kernel compiled + cached before the patch was applied (e.g. warm on-disk cache from a prior BAD run, or a compile that happened earlier in the same process before Minor
Note: I reviewed statically — the sandbox blocked running Python, so I couldn't confirm |
Summary:
Compiled float8 training (e.g. FSDP2 + torch.compile) could intermittently
produce NaN/Inf losses. Root cause is a Triton miscompile: when a kernel
contains
min.NaN/max.NaNPTX instructions, Triton mis-lowers a neighboringtransposed, vectorized 1-byte (fp8) store, writing garbage bytes that decode as
NaN/Inf. The computed value is correct (the contiguous store of the same value
is clean); only the transposed store is corrupted. Filed upstream as
triton-lang/triton#11111.
The bug became visible after PyTorch inductor PR
pytorch/pytorch#186933, which changed the Triton codegen
for min/max from the
tl.where-basedtriton_helpers.{minimum,maximum}totl.{minimum,maximum}(a, b, tl.PropagateNan.ALL). The two forms are numericallyidentical, but only the
PropagateNan.ALLform lowers to themin.NaN/max.NaNinstructions that trip the store bug.
Timeline: nightlies were GOOD through 2026-06-13 and BAD starting 2026-06-14,
bracketing #186933. The failure is flaky (~25%/run) because inductor autotunes
the fused kernel's launch config and only a subset of configs trip the store
miscompile; the miscompile itself is deterministic per config.
This change reverts the inductor min/max codegen back to the numerically-identical
triton_helpers(tl.where) form, which avoids emittingmin.NaN/max.NaN.It is applied from
convert_to_float8_trainingso it only affects users of thefloat8 training product, not every
import torchao. The reverted form is alwayscorrect, so this is a numeric no-op even after Triton fixes the underlying bug.
Test Plan:
Repro'd the FSDP2 float8 + torch.compile NaN in a standalone script; verified the
config that deterministically NaN'd now produces finite losses with the workaround
applied, and confirmed
convert_to_float8_training(but not bareimport torchao)flips the codegen.
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com