Skip to content

Commit 1957123

Browse files
committed
Take cuDNN attention by default only where it measured faster
1 parent 54a899d commit 1957123

2 files changed

Lines changed: 47 additions & 21 deletions

File tree

‎docs/minimax_h3.md‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ stock kernel. `GGML_CUDA_FA_LONGSEQ_NCOLS=128` opts into a wider tile that is fa
5959
but not bit-identical.
6060

6161
Builds configured with `-DGGML_CUDA_CUDNN=ON` (the Linux CUDA prebuilt is) can run that unmasked
62-
attention, and the `--sage-attn` attention, through cuDNN's fused attention on Ampere and newer.
62+
attention, and the `--sage-attn` attention, through cuDNN's fused attention. By default it replaces
63+
the flash attention kernel on Ada (sm89) and B200-class (sm100) GPUs and the sage kernel on sm100 only,
64+
where it measured faster; on an RTX 3090 (sm86) both existing kernels were faster, on an RTX 6000 Ada
65+
the sage kernel was, and other architectures were not measured.
6366
Only `cudnn.h` is needed at build time (`GGML_CUDA_CUDNN_INCLUDE_DIR`; the header-only
6467
cudnn-frontend v1.26.0 is downloaded by CMake unless `GGML_CUDA_CUDNN_FRONTEND_DIR` points at a
6568
checkout). `libcudnn.so.9` is opened when the first attention op runs and is not shipped: put a cuDNN 9
@@ -72,8 +75,9 @@ existing kernels run. The first call of each attention shape builds a cuDNN plan
7275
on a B200, logged once). cuDNN runs F16 with F32 accumulation: for the H3 DiT at 960x544x124 it
7376
takes about 8 ms per attention call on a B200 against 28 ms for the sage kernel and 43 ms for the
7477
ggml flash attention kernel, and it is closer to an exact (fp64) reference than either, so frames
75-
and audio differ slightly from the previous kernels. `GGML_CUDA_CUDNN_ATTN=0` restores them,
76-
`GGML_CUDA_CUDNN_SAGE=0` keeps the sage kernel for `--sage-attn` only, and
78+
and audio differ slightly from the previous kernels. `GGML_CUDA_CUDNN_ATTN=0` restores them and
79+
`GGML_CUDA_CUDNN_ATTN=1` uses cuDNN for the flash attention op on any Ampere or newer GPU;
80+
`GGML_CUDA_CUDNN_SAGE=0` / `=1` does the same for the sage op only, and
7781
`GGML_CUDA_CUDNN_ATTN_BF16=1` runs cuDNN in BF16 (less accurate, same speed).
7882

7983
The video VAE decodes one 16x16 latent tile per decoder graph by default. `SD_H3_VAE_TILE=N`

‎scripts/unsloth/ggml-patches/0006-ggml-cuda-cudnn-attention.patch‎

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
From 80d308615f1dc9fc601f556123861a651b7cffa5 Mon Sep 17 00:00:00 2001
1+
From 784b441f8b098863343e69e0345ef62d2ff580af Mon Sep 17 00:00:00 2001
22
From: Daniel Han <[email protected]>
33
Date: Sat, 3 Oct 2026 18:27:29 -0700
44
Subject: [PATCH] ggml-cuda: optional cuDNN attention for unmasked
@@ -11,7 +11,7 @@ only cudnn.h is needed at build time and libcudnn.so.9 is dlopen'ed on first use
1111
(GGML_CUDA_CUDNN_LIB overrides the name), so the binary has no cuDNN link
1212
dependency and runs unchanged where cuDNN is missing.
1313

14-
On NVIDIA sm80+ it takes unmasked, sink/ALiBi/softcap-free attention with equal
14+
On NVIDIA sm80+ it can take unmasked, sink/ALiBi/softcap-free attention with equal
1515
Q/K/V head sizes (multiple of 8, up to 256) and at least 2^20 attention scores,
1616
GQA and batch included, for FLASH_ATTN_EXT and for SAGE_ATTN in AUTO mode (an
1717
explicitly requested Sage variant keeps its kernel). Q (F32) is narrowed to F16
@@ -22,24 +22,28 @@ kernels' needs). Plans are built once per shape and cached; a shape cuDNN
2222
declines, a failed build or execute, stream capture, Turing and older, or a
2323
missing library keep the existing kernels.
2424

25-
GGML_CUDA_CUDNN_ATTN=0 disables it, GGML_CUDA_CUDNN_SAGE=0 keeps the Sage kernel,
26-
GGML_CUDA_CUDNN_ATTN_BF16=1 runs BF16, GGML_CUDA_CUDNN_ATTN_LOG=1 logs each call.
25+
By default FLASH_ATTN_EXT takes it on sm89 and sm100 and SAGE_ATTN on sm100,
26+
where it measured faster; the existing kernels were faster on sm86 (both ops)
27+
and sm89 (Sage), and other architectures were not measured.
28+
GGML_CUDA_CUDNN_ATTN=0|1 and GGML_CUDA_CUDNN_SAGE=0|1 turn it off or on for every
29+
sm80+ GPU, GGML_CUDA_CUDNN_ATTN_BF16=1 runs BF16, GGML_CUDA_CUDNN_ATTN_LOG=1
30+
logs each call.
2731

2832
test-backend-ops: unmasked cases at the MiniMax-H3 head layout (odd and aligned
2933
lengths, GQA with batch, F32 K/V, permuted Q) and unmasked perf cases.
3034
---
3135
CMakeLists.txt | 3 +
3236
src/ggml-cuda/CMakeLists.txt | 49 ++++
33-
src/ggml-cuda/cudnn-attn-graph.cpp | 258 ++++++++++++++++++++
37+
src/ggml-cuda/cudnn-attn-graph.cpp | 258 +++++++++++++++++++
3438
src/ggml-cuda/cudnn-attn-graph.h | 31 +++
35-
src/ggml-cuda/cudnn-attn.cu | 367 +++++++++++++++++++++++++++++
36-
src/ggml-cuda/cudnn-attn.cuh | 25 ++
39+
src/ggml-cuda/cudnn-attn.cu | 384 +++++++++++++++++++++++++++++
40+
src/ggml-cuda/cudnn-attn.cuh | 26 ++
3741
src/ggml-cuda/fattn.cu | 7 +-
3842
src/ggml-cuda/ggml-cuda.cu | 2 +-
3943
src/ggml-cuda/sage-attn.cu | 12 +-
4044
src/ggml-cuda/sage-attn.cuh | 2 +-
4145
tests/test-backend-ops.cpp | 25 ++
42-
11 files changed, 775 insertions(+), 6 deletions(-)
46+
11 files changed, 793 insertions(+), 6 deletions(-)
4347
create mode 100644 src/ggml-cuda/cudnn-attn-graph.cpp
4448
create mode 100644 src/ggml-cuda/cudnn-attn-graph.h
4549
create mode 100644 src/ggml-cuda/cudnn-attn.cu
@@ -427,10 +431,10 @@ index 00000000..9749f2ab
427431
+ void * q, void * k, void * v, void * o, void * workspace);
428432
diff --git a/src/ggml-cuda/cudnn-attn.cu b/src/ggml-cuda/cudnn-attn.cu
429433
new file mode 100644
430-
index 00000000..b54c29af
434+
index 00000000..a5b9de14
431435
--- /dev/null
432436
+++ b/src/ggml-cuda/cudnn-attn.cu
433-
@@ -0,0 +1,367 @@
437+
@@ -0,0 +1,384 @@
434438
+#include "cudnn-attn.cuh"
435439
+
436440
+#if defined(GGML_CUDA_USE_CUDNN) && !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
@@ -470,11 +474,20 @@ index 00000000..b54c29af
470474
+ return t->nb[0] == ggml_type_size(t->type) && t->nb[1] % 16 == 0 && t->nb[2] % 16 == 0 && t->nb[3] % 16 == 0;
471475
+}
472476
+
477+
+// -1 when unset, otherwise 0 or 1.
478+
+static int cudnn_attn_env_mode(const char * name) {
479+
+ const char * v = std::getenv(name);
480+
+ if (v == nullptr || v[0] == '\0') {
481+
+ return -1;
482+
+ }
483+
+ return cudnn_attn_env_set(name, true) ? 1 : 0;
484+
+}
485+
+
473486
+static bool cudnn_attn_make_plan(int device, const ggml_tensor * dst, cudnn_attn_plan & plan) {
474-
+ static const bool enabled = cudnn_attn_env_set("GGML_CUDA_CUDNN_ATTN", true);
475-
+ static const bool sage = cudnn_attn_env_set("GGML_CUDA_CUDNN_SAGE", true);
487+
+ static const int attn_mode = cudnn_attn_env_mode("GGML_CUDA_CUDNN_ATTN");
488+
+ static const int sage_mode = cudnn_attn_env_mode("GGML_CUDA_CUDNN_SAGE");
476489
+ static const bool want_bf16 = cudnn_attn_env_set("GGML_CUDA_CUDNN_ATTN_BF16", false);
477-
+ if (!enabled || (dst->op == GGML_OP_SAGE_ATTN && !sage)) {
490+
+ if (attn_mode == 0 || (dst->op == GGML_OP_SAGE_ATTN && sage_mode == 0)) {
478491
+ return false;
479492
+ }
480493
+ if (dst->op != GGML_OP_FLASH_ATTN_EXT && dst->op != GGML_OP_SAGE_ATTN) {
@@ -488,6 +501,14 @@ index 00000000..b54c29af
488501
+ if (!GGML_CUDA_CC_IS_NVIDIA(cc) || cc < GGML_CUDA_CC_AMPERE) {
489502
+ return false;
490503
+ }
504+
+ // Unset switches keep the existing kernels except where cuDNN measured faster at H3 shapes: against the flash
505+
+ // attention kernel on sm89 and sm100 (slower on sm86), against the Sage kernel on sm100 (slower on sm86 and
506+
+ // sm89). Other architectures were not measured. =1 opts in on any sm80+ GPU.
507+
+ const bool sm100 = cc >= 1000 && cc < GGML_CUDA_CC_BLACKWELL;
508+
+ const bool sm89 = cc >= GGML_CUDA_CC_ADA_LOVELACE && cc < GGML_CUDA_CC_HOPPER;
509+
+ if (dst->op == GGML_OP_SAGE_ATTN ? sage_mode < 0 && !sm100 : attn_mode < 0 && !sm89 && !sm100) {
510+
+ return false;
511+
+ }
491512
+
492513
+ const ggml_tensor * Q = dst->src[0];
493514
+ const ggml_tensor * K = dst->src[1];
@@ -800,10 +821,10 @@ index 00000000..b54c29af
800821
+#endif
801822
diff --git a/src/ggml-cuda/cudnn-attn.cuh b/src/ggml-cuda/cudnn-attn.cuh
802823
new file mode 100644
803-
index 00000000..f59689d5
824+
index 00000000..067bea63
804825
--- /dev/null
805826
+++ b/src/ggml-cuda/cudnn-attn.cuh
806-
@@ -0,0 +1,25 @@
827+
@@ -0,0 +1,26 @@
807828
+#pragma once
808829
+
809830
+#include "common.cuh"
@@ -814,10 +835,11 @@ index 00000000..f59689d5
814835
+// least 2^20 attention scores is taken; everything else, and every shape cuDNN rejects, keeps the
815836
+// existing kernels.
816837
+//
838+
+// By default FLASH_ATTN_EXT takes it on sm89 and sm100 and SAGE_ATTN on sm100, where it measured faster.
817839
+// Environment:
818-
+// GGML_CUDA_CUDNN_ATTN=0 never use cuDNN for attention
819-
+// GGML_CUDA_CUDNN_SAGE=0 keep the SageAttention kernels for GGML_OP_SAGE_ATTN (AUTO mode;
820-
+// an explicitly requested Sage variant always keeps its kernel)
840+
+// GGML_CUDA_CUDNN_ATTN=0|1 never use cuDNN for attention / use it on every sm80+ GPU
841+
+// GGML_CUDA_CUDNN_SAGE=0|1 keep the SageAttention kernels for GGML_OP_SAGE_ATTN / use cuDNN on every
842+
+// sm80+ GPU (AUTO mode; an explicitly requested Sage variant keeps its kernel)
821843
+// GGML_CUDA_CUDNN_ATTN_BF16=1 run cuDNN in BF16 instead of F16 (F16 K/V are narrowed)
822844
+// GGML_CUDA_CUDNN_LIB=path library to open instead of libcudnn.so.9
823845
+// GGML_CUDA_CUDNN_ATTN_LOG=1 log every call that takes (or falls back from) the cuDNN path

0 commit comments

Comments
 (0)