Repository navigation
perf(attention): rank cuDNN ahead of memory-efficient attention for every model - #9955
Pfannkuchensack wants to merge 9 commits into
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.
|
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.
# Conflicts: # invokeai/app/invocations/krea2/krea2_denoise.py # invokeai/frontend/api/openapi.json
# Conflicts: # invokeai/app/invocations/qwen_image_2_1/qwen_image_2_1_denoise.py
# Conflicts: # invokeai/app/invocations/anima/anima_denoise.py
# Conflicts: # scripts/calibrate_z_image_working_memory.py
Adversarial review of
|
|
@lstein Thanks for the thorough pass.
On the remaining risk: agreed, the fallback only catches errors, not wrong results. The release note will say that images at the same seed change slightly. |
lstein
left a comment
There was a problem hiding this comment.
Approving at f2893206ca.
The PiD fix in 7c62258 is correct: decode attention now goes through the patched F.scaled_dot_product_attention, and nothing else in invokeai/ binds SDPA at import time. Your answers to findings 2–4 are fine as accepted trade-offs, with the shape-scoped demotion as a follow-up.
I merged main into the branch (f289320) to clear the conflicts. All three were mechanical:
sage-attention.mdx: kept this PR's new timings, plusmain's updated Z-Image note ("Also serves ControlNets inside the denoising loop", from #9943).scripts/calibrate_flux2_working_memory.py: tookmain's rewrite (#9927) and kept theinstall_default_sdpa_priority()call and its import.openapi.json: tookmain's config description and re-applied this PR'sattention_backendwording. Compared withmain, the only difference inopenapi.json/schema.tsis that wording.
Checks on the merge: scripts/generate_docs_json.py reproduces settings.json, ruff passes, and tests/backend/util tests/backend/krea2 tests/app/util tests/app/services/session_processor gives 1347 passed, 7 skipped (ROCm, torch 2.13.0+rocm10).
Nit, non-blocking: the install_cudnn_sdpa_fallback docstring (invokeai/backend/util/attention.py, the paragraph naming pixeldit_official.py) and the PR description's "Remaining risks" still say PiD bypasses the fallback.
Summary
PyTorch's own SDPA order is flash, memory-efficient, math, cuDNN. cuDNN ranks behind the always-eligible math kernel, so it is practically never chosen, and Windows CUDA builds have no flash kernel: every model ran on memory-efficient attention. Only Krea-2 and Anima opened their own
sdpa_kernelwindows to reach cuDNN, and those windows are process-global, so they could not hold while two GPUs run sessions at once.This PR installs one process-wide order at startup for every model: flash, cuDNN, memory-efficient, math (
attention_backend: auto).cudnn=yes flash=no efficient=yes math=yes; priority FLASH_ATTENTION > CUDNN_ATTENTION > EFFICIENT_ATTENTION > MATH > OVERRIDEABLE.sdpa_scope.py.INVOKE_KREA2_SDPA_BACKENDkeeps working for benchmarking.Related Issues / Discussions
Related: #9529 (attention optimizations), #9831 (Wan 2.2 A14B at 720p).
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→ 1188 passed, 21 skipped.invokeai/frontend/apicontract tests, docs build,check-docs-data: pass.Denoising time per generation, PyTorch's order → this order (RTX 4090, Windows; median of 12 generations at 1024, 4 at 2048 and video; Wan A14B interleaved A B B A A B, 3 runs each):
Validation on this branch (RTX 4090, graphs built by the UI's own graph builder, one server process, order switched per run, warm-up then torch / new / new / torch):
device_working_mem_gb: 5, new shapes for cuDNN (the documented Windows cuDNN lazy-load hang): FLUX.1 int8 1536²/2048² 34.4/67.8 s, Z-Image int8 1536²/2048² 12.7/25.4 s, Qwen-Image 2.1 1536² 35.7 s; no stalls.Linux CUDA: tested on 2× RTX PRO 6000 Blackwell (Ubuntu 26.04, torch 2.13.0+cu130); results in #9955 (comment).
Not covered: other NVIDIA generations (Ampere), MPS, XPU. The cuDNN fallback's failure path is unit-tested with a failing stand-in kernel; a real cuDNN failure could not be provoked. Z-Image Turbo GGUF could not be run: it fails on
mainalready (TypeErrorinaten.whereon aGGMLTensorin diffusers'transformer_z_image._prepare_sequence), unrelated to this PR.Review
Resolved:
can_use_*, which ignores the priority order; it now asks the dispatcher, so a "yes" cannot route an unexpanded call to math (~9 GB per call at 1024²) when the order is not in force.can_use_*checks.torch.AcceleratorError) and out-of-memory errors are not treated as cuDNN failures.Remaining risks:
scaled_dot_product_attentionat import, before startup patching, so its decode attention bypasses the fallback (it already bypassed the ROCm guard).Compatibility / Rollout
attention_backendkeeps its values (auto,sage); only its description changed.openapi.json,schema.tsand the generated settings docs are regenerated. No persisted data or migrations involved.Checklist
What's Newcopy (if doing a release after this PR)Need help on this PR? Tag
@codesmith-botwith what you need.