Repository navigation
[fa] Make the FlashAttention backend extensible by subclasses - #33426
Merged
Merged
Conversation
klshuster
requested review from
Fridge003,
HaiShaw,
Qiaolin-Yu,
hebiao064,
ispobock and
merrymercy
as code owners
August 3, 2026 20:59
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Fridge003
reviewed
Aug 10, 2026
klshuster
force-pushed
the
kurt/fa4-backend-hooks
branch
from
August 24, 2026 19:18
3cab6d0 to
fa0984d
Compare
klshuster
requested review from
JustinTong0323,
sogalin,
wisclmy0611 and
zijiexia
as code owners
August 24, 2026 19:18
klshuster
force-pushed
the
kurt/fa4-backend-hooks
branch
from
August 24, 2026 19:19
fa0984d to
6077718
Compare
Contributor
Author
|
@Fridge003, I rebased onto current main and cut this down a lot. Dropped |
FA4 already computes the logsumexp for the cascade merge and the backend discards it, and the forwards accept neither return_lse nor **kwargs, so a caller that needs the softmax normalizer has no way to get it. With return_lse=True they return (output, lse), lse in [total_q, num_heads]. Asking for it on a path this does not wire fails on the existing assert lse_out is not None. The 15 kernel invocations now go through the self.flash_attn_* attributes __init__ already assigns, so a subclass can substitute a different FA4 build; that also retires the module-level import those call sites used. Tests: test_fa4.py gains test_return_lse, which calls the forwards directly -- return_lse is a backend contract and the RadixAttention dispatcher's custom-op schema cannot carry it -- and checks the output and the LSE against a causal fp32 reference for MHA extend and GQA decode. Signed-off-by: Kurt Shuster <kurt@thinkingmachines.ai>
klshuster
force-pushed
the
kurt/fa4-backend-hooks
branch
from
September 1, 2026 23:04
6077718 to
3715c29
Compare
Contributor
Author
|
@Fridge003 do you need anything else from me to push this through? just rebased onto main again |
Fridge003
approved these changes
Sep 13, 2026
alexnails
approved these changes
Sep 14, 2026
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.
Motivation
FlashAttentionBackendis hard to specialize without copying it. Three things a subclass may need are not exposed today, leaving only the options of duplicating the backend or patching module globals: the logsumexp the kernel already computes, per-layer control of split-KV, and the choice of FA4 build. This adds a seam for each. All three are no-ops for current behavior.Modifications
return_lseonforward_extend/forward_decode. The kernel already computes the logsumexp for the cascade merge and the backend discards it, and the forwards accept neitherreturn_lsenor**kwargs, so a caller that needs the softmax normalizer has no way to get it. Withreturn_lse=Truethey return(output, lse),lseshaped[total_q, num_heads](the varlen kernel emits it head-major)._check_return_lseraises on the combinations the paged path cannot serve — non-FA4, MLA,fa_skip_kv_cache, cross-attention, context parallelism, local and cascade attention — instead of returning a quietly wrong tensor. Defaults toFalse._resolve_num_splits(layer, forward_batch, is_swa_layer). Split-KV was read fromself.num_splitsat 13 kernel arguments, making it one value for the whole run. It is now resolved once per forward and threaded through, so a subclass can vary it per layer or per batch — for example forcing1on sliding-window layers, which degenerate when split. The base implementation returnsself.num_splits. Since FA3'sscheduler_metadatais precomputed fromself.num_splits, the existingsched_metagate also requiresnum_splits == self.num_splitsso a subclass cannot reuse a schedule built for a different count; always true for the base class.Kernel entry points as class attributes.
flash_attn_with_kvcacheandflash_attn_varlen_funcare bound on the class and the 15 invocations go throughself._flash_attn_*, so a subclass can substitute a different FA4 build.Accuracy Tests
Two cases added to
test/registered/attention/unittests/dense/test_fa4.py, self-contained so the shared dense kit is untouched:test_return_lse— calls the forwards directly, sincereturn_lseis a backend-level contract and theRadixAttentiondispatcher's custom-op schema cannot carry it, then checks the output and the LSE against a causal fp32 reference for MHA extend and GQA decode, plus the[total_q, num_heads]shape.test_return_lse_rejects_unsupported_paths— the guarded paths raise, and the check is a no-op when the LSE is not requested.Both pass on a GB300 with no skips. Perturbing the returned LSE by a constant fails
test_return_lseon 100% of elements, confirming the comparison is load-bearing.Not covered: windowed layers, because the dense harness leaves
attention_chunk_sizeunset, so asliding_window_sizecase is skipped forfa4before it asserts anything. The FA3sched_metagate was also not exercised, as the FA3 dense suite skips on that hardware; it is inert for the base class, whose hook returnsself.num_splits.Speed Tests and Profiling
Not applicable. No behavior changes for existing callers:
return_lsedefaults toFalse,_resolve_num_splitsreturnsself.num_splits, the_flash_attn_*attributes are bound to the same functions the module already imported, and the newsched_metacondition is always true unless a subclass varies the split count.Checklist
CI States
Latest PR Test (Base): ⏳ Run #34800666242
Latest PR Test (Extra): ❌ Run #34800666040
Latest PR Test (AMD ROCm 10): ⏳ Run #34800666284