Thread tanh logit softcapping through FlashAttention (FA2, opt-in FA3) - #3391
Open
nvegesna-netizen wants to merge 10 commits into
Open
Thread tanh logit softcapping through FlashAttention (FA2, opt-in FA3)#3391nvegesna-netizen wants to merge 10 commits into
nvegesna-netizen wants to merge 10 commits into
Conversation
This was referenced Aug 17, 2026
Contributor
Greptile SummaryThe PR adds configurable tanh attention-logit softcapping and carries it consistently through backend selection, unfused attention, FlashAttention 2 context-parallel execution, and eligible FlashAttention 3 execution.
Confidence Score: 5/5The PR appears safe to merge with no blocking failure remaining from the previously reported issues. No blocking failure remains. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[DotProductAttention request] --> B{softcap nonzero?}
B -- No --> C[Existing backend selection]
B -- Yes --> D[Disable FusedAttention and FA4]
D --> E{FA3 capable, non-CP, head dimensions <= 256?}
E -- Yes --> F[FA3 with softcap]
E -- No --> G{FA2 >= 2.6 and supported dropout mode?}
G -- Yes --> H[FA2 with softcap]
G -- No --> I{Context parallel?}
I -- No --> J[Unfused attention with softcap]
I -- Yes --> K[No eligible backend: raise]
Reviews (12): Last reviewed commit: "test: add softcap no-op and closed-form ..." | Re-trigger Greptile |
nvegesna-netizen
force-pushed
the
nvegesna/gemma2-softcap-core
branch
from
August 17, 2026 21:21
a6a793b to
5917f0d
Compare
nvegesna-netizen
force-pushed
the
nvegesna/gemma2-softcap-core
branch
from
August 17, 2026 21:35
19a21eb to
5ae46ce
Compare
cyanguwa
reviewed
Aug 26, 2026
Collaborator
|
Please follow this link to fix the DCO of this PR as well. Thanks! https://github.com/NVIDIA/TransformerEngine/pull/3391/checks?check_run_id=97902991433 |
…in FA3) Add a user `softcap` value (tanh logit softcapping, `softcap*tanh(x/softcap)`) to DotProductAttention so models like Gemma2 can run on the fused flash path instead of an unfused/FlexAttention kernel. - Add `softcap` to DotProductAttention (init+forward) and AttentionParams; thread it into the FA2 non-CP kwargs and all three context-parallel autograd functions (forward + ctx-saved backward). softcap=0.0 reproduces prior behavior. - get_attention_backend: when softcap != 0, disable FusedAttention/unfused and steer to FA2 -- disable FA3/FA4, and disable FA2 < 2.6.0 -- so the cap is never silently dropped (FA2 < 2.6.0) or hit at runtime as NotImplementedError (FA3/FA4). Also disable FA3 under context parallelism (its CP path hard-rejects nonzero softcap) so CP+softcap steers to FA2, which supports it, instead of crashing. - FA3 softcap opt-in: NVTE_FA3_SOFTCAP=1, Hopper (sm90) hd<=256, non-CP only, gated on a fail-closed signature probe (fa3_supports_softcap). Forward threads softcap into fa_3_optional_forward_kwargs; the existing Hopper autograd function carries it into backward automatically. Default off; unchanged behavior steers to FA2. - ONNX export: fail loudly (assert) rather than silently drop softcap -- export unconditionally force-selects UnfusedDotProductAttention, which has no softcap support, so this previously exported models with softcapping silently omitted. - Tests: test_softcap.py (FA2 fwd/bwd parity vs pure-PyTorch reference), wired into qa/L0_pytorch_unittest/test.sh. FA4 softcap opt-in is deliberately NOT included here -- see follow-up PR. On Blackwell (SM100), FA4's dedicated head_dim=256 forward kernel has no score_mod support at all (kernel constructor asserts `score_mod is None`), so there is currently no FA4 kernel path this could opt into; adding the scaffolding now would just be inert code with nothing to exercise. Addresses review findings: CP+FA3 softcap selection crash, ONNX silent drop, and the missing CI wiring for test_softcap.py. Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
python -O / PYTHONOPTIMIZE strips assert statements, which would silently reopen the ONNX export softcap-drop bug the previous commit fixed (ONNX mode would again force-select UnfusedDotProductAttention with softcap silently omitted, with no error). Switch to an explicit if/raise ValueError, which survives optimized execution. Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
for more information, see https://pre-commit.ci (reapplied after a force-push rebase clobbered pre-commit.ci's original 19a21eb commit; same content, restored by hand) Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com>
…fold test into test_attention.py UnfusedDotProductAttention now applies softcap * tanh(scores / softcap) to the already-scaled logits, matching how FlashAttention folds softmax_scale into its tanh argument, so it can serve as the softcap reference backend. Backend selection therefore no longer disqualifies unfused attention for softcap, and the ONNX-export guard is dropped since the export path force-selects unfused and torch.tanh is exportable. test_softcap.py is replaced by a model_configs_softcap dict and test_dpa_softcap in test_attention.py, which reuses test_dot_product_attention for backend sweeping. Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com>
Drop the redundant NVTE_FA3_SOFTCAP opt-in. `use_flash_attention_3` already derives from NVTE_FLASH_ATTN_V3, so the existing flag governs the FA3 softcap path and NVTE_FLASH_ATTN_V3=0 disables it. Correctness stays established by the build-capability probe, head_dim <= 256, and the non-CP requirement. Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com>
FA4 exposes no softcap kwarg and its head_dim=256 kernel asserts score_mod is None, so there is no kernel to route the cap through. The FA4 call path in backends.py passes no softcap, so an FA4 selection with a nonzero softcap silently dropped the cap instead of failing closed. NVTE_FLASH_ATTN_V4 defaults to enabled, so this was reachable on SM100+ with flash-attn v4 installed and no context parallelism. Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com>
flash-attn rejects a nonzero softcap combined with nonzero dropout at dispatch: "Softcapping does not support dropout for now" in csrc/flash_attn/flash_api.cpp, present in mha_fwd and mha_varlen_fwd from v2.6.0 (the earliest version TE allows softcap on) onwards. Backend selection did not model this, so a softcap + attention-dropout config passed selection, routed to FA2, and crashed inside flash-attn. Dropout only reaches the kernel while training, since backends.py passes `self.attention_dropout if self.training else 0.0`, so the gate is on `attention_dropout != 0.0 and is_training` to avoid blocking valid inference configs. UnfusedDotProductAttention supports both softcap and dropout and stays available, so this steers rather than hard-fails. Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com>
nvegesna-netizen
force-pushed
the
nvegesna/gemma2-softcap-core
branch
from
August 27, 2026 16:57
82a2bf4 to
5ecabac
Compare
test_dot_product_attention forced is_training=False whenever FusedAttention could not train a config, so that backends only available for inference could still be compared. softcap always disables FusedAttention, so test_dpa_softcap silently degraded to a forward-only comparison and the PR's backward-parity claim -- the FA2 softcap backward kernel included -- went untested. Add fwd_only_without_fused_attn (default True, so every other caller is byte-for-byte unchanged) and opt test_dpa_softcap out, which pairs FlashAttention against UnfusedDotProductAttention with is_training=True and restores the dgrad comparison. Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com>
Two gaps remained after folding test_softcap.py into test_attention.py. softcap=0.0 no-op: every model_configs_softcap entry uses a nonzero cap, so nothing asserted the backward-compatibility claim. The half that the PR actually changed is backend selection, and a filter that fired at 0.0 would silently remove FusedAttention and FA4 from other tests rather than fail one. test_dpa_softcap_zero_backend_selection asserts FusedAttention survives softcap=0.0 and is disabled by a nonzero cap. Unfused coverage and tanh's nonlinear region: test_dpa_softcap needs two TE backends, so it skips entirely without flash-attn even though UnfusedDotProductAttention now implements softcap and is the reference for everything else. It also cannot detect a dropped cap at all: 0.1 * randn inputs put the logits at O(1e-2), where the reference output moves by 9e-9 at cap=50 and 2e-4 at cap=0.01. test_dpa_softcap_vs_reference compares forward and dQ/dK/dV against a pure-PyTorch oracle one backend at a time, so it runs with unfused alone, and uses randn inputs so the cap moves the output by O(1). An assertion on that displacement keeps the test from going vacuous if the config drifts. Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com>
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.
Adds a
softcapkwarg toDotProductAttentionso models with attention-logit soft-capping (cap·tanh(x/cap), e.g. Gemma2) can run on TE's fused flash-attention kernels instead of falling back to an unfused/non-TE path.Companion PRs (needed together for an end-to-end model to pick this up):
TransformerConfig.attn_logit_softcappingand maps it to thissoftcapkwargattn_logit_softcappingWhat changed
DotProductAttention(init + forward) andAttentionParamsgain asoftcap: float = 0.0kwarg.softcap=0.0is a no-op — existing behavior is unchanged.UnfusedDotProductAttentionappliescap * tanh(scores / cap)to the already-scaled logits, so it serves as the in-tree reference implementation (and the numerical reference the tests compare the flash backends against). With qk-layer-scaling thelayer_numberfactor is divided out of the cap, since that path defers scaling to the softmax.get_attention_backend: whensoftcap != 0, FusedAttention (cuDNN), FA4, and FA3 (unless the checks below pass) are disqualified and selection steers to FA2 or unfused (also disqualifies FA2 builds too old to carry the softcap kernel). This is a deliberate safety net — softcap must never be silently dropped, nor hit aNotImplementedErrorat runtime.softcappresent in both FA3 entry points),max(head_dim_qk, head_dim_v) <= 256, and non-CP; forward threadssoftcapinto FA3's kwargs, backward is handled automatically by the existing Hopper autograd function. No new env var — FA3 eligibility is governed by the existingNVTE_FLASH_ATTN_V3(default1). Since TE already prefers FA3 over FA2 on sm90, FA3 is the default softcap backend on Hopper when a softcap-capable FA3 build is installed; that is intentional.NVTE_FLASH_ATTN_V3=0steers to FA2.tests/pytorch/attention/test_attention.py.test_dpa_softcapsweeps the available backends through the existingtest_dot_product_attentionharness (forward + backward parity against the unfused reference); softcap always disqualifies FusedAttention, so it opts out of the harness's fused-unavailable fallback to keep the dQ/dK/dV comparison.test_dpa_softcap_zero_backend_selectionassertssoftcap=0.0leaves FusedAttention selectable and a nonzero cap does not.test_dpa_softcap_vs_referencecompares forward and dQ/dK/dV against a closed-form pure-PyTorch oracle one backend at a time, soUnfusedDotProductAttentionstays covered on machines without flash-attn; it uses its ownrandninputs because the shared harness's0.1 * randnputs logits at O(1e-2), where a cap of 50 moves the output by ~1e-8 and a dropped cap would be undetectable.Supported configurations
flash-attn >= 2.6.0(first FA2 release exposing asoftcapkwarg). The default path wherever FA3 is not eligible (i.e. off sm90, or no softcap-capable FA3 build). Requires zero attention dropout while training — see below.cp_comm_typevalues:p2p,all_gather,a2a,a2a+p2p(p2panda2a+p2pshare one autograd function). Sameflash-attn >= 2.6.0requirement; forward and backward both carrysoftcap.flash_attn_func/flash_attn_varlen_funcboth exposesoftcap(signature probe, fail-closed) andmax(head_dim_qk, head_dim_v) <= 256. FA3 is Hopper (sm90)-only upstream and governed by the existingNVTE_FLASH_ATTN_V3(default1). When eligible it takes precedence over FA2 on Hopper — deliberate; both paths were exercised on Hopper (see Validation). Any check failing steers to FA2 (>= 2.6.0) or unfused.Not supported:
get_attention_backendwhensoftcap != 0.0, so selection steers to FA2 (or unfused) rather than silently dropping the cap.NotImplementedError(use FA2).get_attention_backendwhensoftcap != 0.0; without that it would be selected on SM100 (NVTE_FLASH_ATTN_V4defaults to1) and silently drop the cap. See the follow-up section below.csrc/flash_attn/flash_api.cpp), so FA2 is disqualified and selection steers to unfused. Dropout reaches the kernel as0.0in eval, so inference configs are unaffected. Since unfused does not support CP, CP + softcap + dropout while training has no eligible backend and raises rather than crashing inside flash-attn.ONNX export force-selects the unfused backend, which now honors
softcapviatorch.tanh(exportable as the ONNXTanhop), so export is expected to work — but there is no ONNX softcap test in this PR.softcap = 0.0(the default) disables softcapping: backend selection and numerics are identical to today.Validation
test_dpa_softcap— forward and backward parity against the unfused reference, across whichever backends are available on the test machine.test_dpa_softcap_vs_reference— forward and dQ/dK/dV against a closed-form reference, forsoftcapin{0.0, 0.5}, with logits at O(1) sotanhruns in its saturating region.softcap=0.0compares against a reference that never appliestanh, and the nonzero case asserts the cap moves the reference output by more than the comparison tolerance, so the test cannot pass an implementation that drops the cap.test_dpa_softcap_zero_backend_selection— thesoftcap=0.0no-op claim for backend selection, which is the half the filter actually changed.On the Hopper target both the FA2 and the FA3 softcap paths were exercised. FA3 taking precedence over FA2 there is an intentional design choice.
Follow-up, not in this PR: FA4
FA4 softcap support is deliberately excluded here rather than included as dead scaffolding. On Blackwell (SM100), FA4's dedicated
head_dim=256forward kernel has noscore_mod/softcap fusion logic in it at all — the kernel constructor assertsscore_mod is None. So there's currently no FA4 kernel path capable of serving this shape; adding an opt-in flag now would just be inert code with nothing to opt into. This follows as its own PR (stacked on this branch) once — or if — an FA4 kernel with softcap fusion forhead_dim=256lands.