Repository navigation
feat(attention): let attention_backend pin an SDPA kernel - #9956
Conversation
…very model One process-wide PyTorch SDPA order (flash, cuDNN, memory-efficient, math) replaces the Anima and Krea-2 windows; 4–18 % faster denoising on Windows CUDA, unchanged on ROCm. A cuDNN runtime failure demotes cuDNN for the process and retries the call; the attention log names the active order once per GPU. SageAttention docs measure against the new default.
Expected priority orders are chosen by the layout of torch's own order; torch 2.7 lists MATH in its fifth slot instead of OVERRIDEABLE. Grouped-query checks on CPU skip or adapt where torch 2.7's CPU flash kernel does not take enable_gqa; the attention log names each backend once.
`attention_backend` accepts cudnn, flash, efficient and math: the kernel is ranked first, never exclusive, and a call it cannot serve is logged once per node type and reason. Krea-2 expands its grouped-query heads when that lets the pinned kernel serve them; `auto` is unchanged.
Which kernel torch's CPU dispatcher picks changes between releases (torch 2.14's flash takes 3-D inputs), so the tests force math with an exclusive window and ask torch before relying on flash.
|
Linux CUDA test run. Machine: Verda 2× NVIDIA RTX PRO 6000 Blackwell Server Edition (96 GB, sm_120), driver 580.178.04, Ubuntu 26.04.1, torch 2.13.0+cu130, cuDNN 9.20, Triton 3.7.1. The PR head was compared with its merge base on
|
Call _process without the removed poll_now_event and keep the set_device patch.
…rnel Call _process without the removed poll_now_event and keep the set_device patch.
# Conflicts: # invokeai/app/invocations/krea2/krea2_denoise.py # invokeai/frontend/api/openapi.json
…rnel # Conflicts: # invokeai/app/invocations/krea2/krea2_denoise.py # invokeai/frontend/api/openapi.json
# Conflicts: # invokeai/frontend/api/openapi.json
# Conflicts: # invokeai/app/invocations/qwen_image_2_1/qwen_image_2_1_denoise.py
lstein
left a comment
There was a problem hiding this comment.
Reviewed at ce27916ebc, this PR's own delta on top of #9955 (fc00e2bcde..ce27916ebc). The design holds up well overall. One change is requested, plus one nit.
1. attention_backend: math is not budgeted for most model families (change requested)
invokeai/backend/util/attention.py (sdpa_score_matrix_bytes)
The PR says "the working-memory estimate follows the dispatcher and prices it". That holds only where an estimator calls sdpa_score_matrix_bytes:
- FLUX.2, Krea-2, Qwen-Image, Qwen-Image-2.1 and Z-Image denoise
- the VAE paths (
vae_working_memory.py), the Qwen-Image-2.1 text encoder and MOSS
SD1.5/SDXL, FLUX.1, Anima, Wan, LTX-2, CogView4, ERNIE, MiniMax H3 and Ideogram4 reserve nothing for a score matrix.
How to trigger it: on CUDA, set attention_backend: math and run FLUX.1 at 1024². That is 24 heads × 4608² × 17 B ≈ 8.1 GiB per attention call, all of it unreserved, so the model cache under-reserves and the generation OOMs. Wan 2.1 at 480p with 81 frames (~32.8k tokens, 40 heads) needs about 680 GiB per call. On ROCm the guard's chunking bounds this, so the problem is CUDA-only.
math is documented as a debugging mode that "needs much more memory", but the claim that it is priced reads as a guarantee. Any one of these would resolve it:
- Narrow the PR text and the
attention_backenddescription to the families that are actually priced. - Log a startup warning when
mathis pinned, saying the working-memory estimates don't cover it for the other families. - Price it in the remaining estimators.
2. Nit: attention that runs on the CPU is reported
_pinned_kernel_bypass returns ("device", "cpu"). With cudnn or efficient pinned, a CPU-only install logs one warning per node type ("on cpu PyTorch picks the kernel itself, without the ranking"). So does a GPU install whenever a node's attention runs on the CPU. The message is accurate, but the pin was never meant to apply on the CPU. Skipping the report for CPU tensors, the same way MPS is already skipped, would remove the noise.
Checked and found sound
- Wrapper order (ROCm guard → report → cuDNN fallback → sage): guard chunks aren't reported once per chunk, and a retry after cuDNN is demoted is reported as
demoted. - cuDNN demotion while cuDNN is pinned: flash ends up first again, so Krea-2 goes back to its old decision.
- Krea-2 grouped-vs-expanded decision under each pin: on ROCm (W7900, torch 2.13+rocm10) I compared the expanded-call probe with the real call for
efficient/cudnn/math× 3 sequence lengths (odd lengths included) × 4 mask shapes. They never disagreed. attention_caller: there is a single node-execution site, and the value is reset when a node raises.- PyTorch's debug reason: it goes to stderr as a C++ warning, so the global
SilenceWarningsin SD/SDXL denoise doesn't hide it. - Calibration:
attention_backendis already part of the calibration fingerprint. - Tests: the focused suites pass on ROCm Linux (313 passed, 7 skipped).
Not checked here: CUDA-only paths (flash missing on Windows, cuDNN demotion, the probe on cuDNN) and XPU.
# Conflicts: # invokeai/app/invocations/anima/anima_denoise.py
…/sdpa-pinned-kernel
Pinning math logs a startup warning and the config and docs say most working-memory estimates omit the score matrix. The pinned-kernel report skips CPU tensors.
|
@lstein Thanks, both addressed in ae27189 (also merged with main).
|
# Conflicts: # scripts/calibrate_z_image_working_memory.py
…/sdpa-pinned-kernel
lstein
left a comment
There was a problem hiding this comment.
Re-reviewed at 4b951225d1. Both points from the previous review are resolved in ae27189c42, and the other new commits are merges that add nothing else to this PR's changes. Approving.
mathand working memory. Pinningmathnow logs a startup warning. The warning, theattention_backenddescription, the generated docs and the OpenAPI schema all say that most families' working-memory estimates leave out the score matrix. The warning is only logged once the pinned order is actually in place, which is the right condition.- CPU reports. The report now covers only
cuda/xpu. CPU attention is skipped, so it is no longer reported per node type and no longer pays for the per-call kernel query. The replacement test covers the skip, and the remaining report tests treat the CPU as a stand-in GPU.
Checked:
- The focused suites (pinned report, sdpa_scope, Krea-2, node attribution, config, cuDNN fallback, ROCm guard, backend choice) pass on ROCm Linux (W7900, torch 2.13+rocm10): 317 passed, 7 skipped.
- The new tests catch the bugs they cover: putting the CPU back on the report list, or removing the
mathwarning, makes the matching new test fail.
Nit (description only, no code change needed): the PR body still says the math working-memory estimate "follows the dispatcher and prices it". Please reword that to match the new warning.
Not checked here: the CUDA-only paths and XPU.
# Conflicts: # docs/src/content/docs/configuration/Optimization/sage-attention.mdx # invokeai/app/invocations/qwen_image_2_1/qwen_image_2_1_denoise.py # invokeai/app/services/session_processor/session_processor_default.py # invokeai/frontend/api/openapi.json # scripts/calibrate_flux2_working_memory.py
Summary
Builds on #9955 (one process-wide SDPA order, cuDNN ranked ahead of memory-efficient attention). Until #9955 merges, this branch contains its commits; the change of this PR is the last commit, f8a93c0.
#9955 gives
attention_backend: autoa fixed order (flash, cuDNN, memory-efficient, math). This PR lets the server admin pin one PyTorch kernel instead, ininvokeai.yaml:auto's order. It is never exclusive and never raises: a call it cannot serve runs on the next kernel.attention_backend 'efficient' is not possible for attention in qwen_image_2_1_text_encoder: the memory-efficient kernel cannot serve this call (bfloat16, head_dim 128, no mask, 32 query / 8 key-value heads, enable_gqa, causal); it runs on CUDNN_ATTENTION.PyTorch's own reason follows once on stderr. Other reasons: the build or GPU has no such kernel (e.g.flashon Windows CUDA,cudnnon ROCm, also warned at startup), cuDNN was demoted after a runtime failure, an exclusiveINVOKE_KREA2_SDPA_BACKENDwindow switched it off, or ROCm's wide-head guard sent the call to math.efficientreally runs Krea-2 on memory-efficient attention instead of cuDNN.mathmaterializes the full score matrix (slow, much more memory) and is meant for debugging; the working-memory estimate follows the dispatcher and prices it.autoandsageinstall nothing new and behave exactly as with perf(attention): rank cuDNN ahead of memory-efficient attention for every model #9955. Withsage, the calls SageAttention does not serve (text encoders, VAEs, masked and short calls) useauto's order.Use case: an escape hatch from cuDNN (e.g. a GPU/driver combination where it misbehaves or hangs, which #9955's automatic fallback cannot catch), and benchmarking or support questions.
Related Issues / Discussions
Depends on #9955.
QA Instructions
Tests
CUDA_VISIBLE_DEVICES=-1 uv run --no-sync pytest tests/backend/util tests/backend/krea2 tests/app/util tests/app/services/session_processor tests/test_config.py→ 1288 passed, 35 skipped.invokeai/frontend/apicontract tests: pass.openapi.json,schema.tsand the generated settings docs are regenerated.End to end (graphs built by the UI's own graph builder; the kernel each call ran on counted through the dispatcher):
efficientmathflashcudnnautoautosageRepeats were bit-identical within every setting.
Linux CUDA: tested on 2× RTX PRO 6000 Blackwell (Ubuntu 26.04, torch 2.13.0+cu130); results in #9956 (comment).
Not covered: other NVIDIA generations (Ampere), macOS (the report skips devices the dispatcher cannot answer for; the MPS test needs Apple Silicon), XPU.
Review
Resolved:
efficientstill ran Krea-2 on cuDNN; it now expands when the expanded call gets a higher-ranked kernel.autodid extra dispatcher work per Krea-2 call; with flash ranked first (auto,flash, PyTorch's order, after a cuDNN demotion) the decision is the previous one, unchanged.Remaining limits:
scaled_dot_product_attentionat import, before startup patching: its attention follows the pinned order but is not reported.INVOKE_KREA2_SDPA_BACKENDwindow toggled twice within the probe; that variable is a benchmarking tool.Compatibility / Rollout
attention_backendgains four values; the defaultautois unchanged, and the setting stays startup-only (not runtime-writable). The OpenAPI schema and generated types change accordingly; the frontend does not read this field.Checklist
What's Newcopy (if doing a release after this PR)Need help on this PR? Tag
@codesmith-botwith what you need.