Repository navigation
[Fix] Let fx.maxnumf inherit the ambient fastmath - #1035
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a performance regression introduced when kernels were migrated to fx.maxnumf by ensuring fx.maxnumf inherits the ambient fastmath flags from tracing_context (unless an explicit fastmath= is provided), matching the behavior of other DSL operators.
Changes:
- Update
fx.maxnumfto resolvefastmathviaresolve_fastmath()and forward it to the raw MLIR builder only when effective flags are present. - Add backend-agnostic IR-string unit tests to guard ambient fastmath inheritance and explicit override behavior for
fx.maxnumf. - Pin a few MLA
fx.maxnumfcall sites tocontractfastmath explicitly to avoid widening behavior in a path that can’t currently be validated on the author’s machine.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
tests/unit/test_fastmath_context.py |
Adds IR-level unit tests verifying fx.maxnumf inherits ambient fastmath, explicit overrides work, and flags don’t leak outside a block. |
python/flydsl/expr/arith.py |
Makes maxnumf() resolve ambient fastmath (when unset) and forward the effective flag into arith.maxnumf. |
kernels/attention/mla_fwd_decode_m16x8_fp8_fp8.py |
Pins fx.maxnumf(..., fastmath=contract) at key reduction/max sites pending validation of widening. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Right — it was already in the import list; the added entry was redundant and out of alphabetical order. Removed. Pushed. |
fx.maximumf and fx.minimumf carry @dsl_math_wrap_result and a fastmath parameter, and the decorator fills that parameter from the ambient context when the caller leaves it unset. fx.maxnumf had neither: it took **kwargs, hand-rolled its own result wrapping, and passed straight to the raw builder, so the op came out fastmath<none> whatever the enclosing kernel asked for. #930 moved the attention kernels onto this function and the flag was dropped at every site it touched -- 29 across attention, paged decode, MLA and moe. On the gfx950 dense bf16 path that costs 9-11% at long sequence lengths, and nothing failed. Give it the same shape as its neighbours. The hand-written Vector/Numeric wrapping goes away with it. MI355X, B=2 H=32 D=128 bf16 non-causal, cache cold, two rounds: S main with this 61,380 1232-1235 1233-1234 180,180 1076-1079 1174-1175 +9.0% What each affected path inherits, and how it was checked: attention (flash, 16 sites) fast bit-identical over four input distributions including a planted NaN; 87 tests pass paged decode (22 sites) contract 2304 tests, identical results moe (2 sites) none unchanged, no hint to inherit MLA fp8 (3 sites) fast pinned to contract here The MLA sites are pinned rather than widened because tests/kernels/ test_mla_decode.py cannot run against the aiter in this image, so the change cannot be verified there. contract is inert for a max, matching what #930 did for the paged path. Drop the argument once that test runs. Three tests are added next to the existing ambient-fastmath ones, covering maxnumf, maximumf and minimumf: each inherits the ambient flags, an explicit argument overrides them, and no flag appears outside the block. They are IR-string checks and need no GPU. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9ccb52f to
ae902c0
Compare
|
LGTM |
#1035 landed, so fx.maxnumf picks the flag up from the launcher's compile hints like the three neighbouring calls in this file already do.
#1035 landed, so fx.maxnumf picks the flag up from the launcher's compile hints like the three neighbouring calls in this file already do.
|
Post-merge follow-up from a three-pass review. Two items should be fixed before the next 1. Restore the stable
|
|
Both fixed in #1050. Contract — hand-wrapped again with ninf — the 16 sites carry Worth noting for the perf question: the speed was entirely in Guards added: an IR check that the flag emits You are right about the census — 16 in |
…ductions Two follow-ups to #1035. fx.maxnumf is in the stable catalog and returns the DSL type of a, but the bare dsl_math_wrap_result it moved to does not: a keyword binding came back as a raw ArithValue, a logical (2, 2) came back flat, and a generic vector dtype came back concrete. Hand-wrap it again and resolve the ambient inside, which is exact parity plus the flag. Tests cover keyword scalars, keyword vectors and generic vector dtype; they fail on main. The 16 maxnumf sites in flash_attn_utils.py were inheriting the ambient fast, which carries ninf, while _score_pair_max is seeded with literal -inf and masked scores are -inf too -- an infinite operand under ninf is poison. Pass nnan explicitly instead, the same flag and the same reasoning pa_decode_tile.py already records. swa_gfx950.py masks with -inf as well and now uses it rather than the ambient it was reaching for. nnan is where the speed was: 1213.5 / 1207.2 TFLOP/s against main's 1210.3 / 1208.3 at S=61,380 / 180,180, two rounds, so #1035's recovery is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
fx.maxnumfcannot see the ambient fastmath. It forwards straight to the raw MLIR builder, so with no explicit argument the op comes outfastmath<none>whatever the enclosing kernel asked for. #930 moved the kernels onto this function and the flag was dropped at every site it touched — 29 of them across attention, paged decode, MLA and moe. On the gfx950 dense bf16 path that costs 11.1–11.9% at long sequence lengths, and nothing failed.Root cause
fx.maximumfandfx.minimumfcarry@dsl_math_wrap_resultand afastmathparameter, and the decorator fills that parameter from the ambient context when the caller leaves it unset:fx.maxnumfhad neither. It took**kwargs, hand-rolled its ownVector/Numericwrapping, and passed straight to the raw builder, so the op came outfastmath<none>whatever the enclosing kernel asked for. #930 moved the attention kernels onto it and the flag was dropped at all 29 sites it touched.This gives it the same shape as its neighbours; the hand-written wrapping goes away with it.
Result
MI355X, B=2 H=32 D=128 bf16 non-causal, stock kwargs, two rounds each, cache cold (
FLYDSL_RUNTIME_ENABLE_CACHE=0), aiter measured in the same process as a control:Six timed rounds per point, alternating arms, idle machine, spread under 0.6% within each arm.
The step lands on #930 in a bisect:
47009a3(8/14) measures 1188.6 / 1183.4 at the two long lengths,daee90e(#930, 8/17) measures 1077.0 / 1060.9, and everything between then and now is flat.What each path inherits, and how it was checked
fastndiff=0/67108864, matching NaN masks, rel L2 identical to seven digits. 87 tests pass.contracttest_sliding_window_accuracy[...fp8]cases that already fail onmain.contractpermits only FMA fusion, which is inert for a max.fastcontractin this PR rather than widened.The MLA sites are pinned because
tests/kernels/test_mla_decode.pyaborts on this machine (Fatal Python error: Aborted, identically with and without this change), so the widening cannot be verified there. Pinning keeps them exactly where they are today;contractis inert for a max, and it matches what #930 did for the paged path. The argument should be dropped once that test runs.Worth noting for whoever picks that up:
test_pa.pyandtest_mla_decode.pyskip silently when the installed aiter does not match flydsl (cannot import name 'fly_values' from 'flydsl.compiler.protocol'), collecting zero of 2305 tests and exiting 0. The runs above went through a shim that stubsaiter.ops.flydslso the suites actually execute.Guard
Three tests next to the existing ambient-fastmath ones, parameterised over
maxnumf,maximumfandminimumf: each inherits the ambient flags, an explicit argument overrides them, and no flag appears outside the block. Onmainthemaxnumfcase fails and the other two pass, which is the asymmetry this fixes. IR-string checks markedl0_backend_agnostic, so CI runs them without a GPU.Testing
main), moe unchanged, DSL unit file 17.Breaking Changes
None, on everything that could be measured: bit-identical on the attention path, identical test results on paged decode and moe, and MLA held at its current flags.