[Attention] FlashAttention 4 SM100 FP8 kv cache support (#42569)
Signed-off-by: Matthew Bonanni <mbonanni@redhat.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Matthew Bonanni committed
Jul 20, 2026 at 13:53 UTC
bd091079cba0800d8b8ee8ed22feab5864d1b101
8 files changed
+194
-33
cmake/external_projects/vllm_flash_attn.cmake
+1
-1
@@ -39,7 +39,7 @@ else()
39
FetchContent_Declare(
40
vllm-flash-attn
41
GIT_REPOSITORY https://github.com/vllm-project/flash-attention.git
42
- GIT_TAG caaa4eb59845388a20b1f435ecaafb4bd9517ad8
42
+ GIT_TAG 168920233059c48de6199e2cda74003b2ce3d199
43
GIT_PROGRESS TRUE
44
# Don't share the vllm-flash-attn build between build types
45
BINARY_DIR ${CMAKE_BINARY_DIR}/vllm-flash-attn
docs/design/attention_backends.md
+2
-2
@@ -164,8 +164,8 @@ Priority is **1 = highest** (tried first).
164
| `FLASHINFER` | XQA† | fp16, bf16 | `auto`, `float16`, `bfloat16`, `fp8`, `fp8_e4m3`, `fp8_e5m2` | 16, 32, 64, 128, 256, 512, 1024 | 64, 128, 256, 512 | ❌ | ❌ | ❌ | ✅ | Decoder | 9.0 |
165
| `FLASHINFER` | trtllm-gen† | fp16, bf16 | `auto`, `float16`, `bfloat16`, `fp8`, `fp8_e4m3`, `fp8_e5m2`, `nvfp4` | 16, 32, 64, 128, 256, 512, 1024 | 64, 128, 256, 512 | ✅ | ✅ | ❌ | ✅ | Decoder | 10.x |
166
| `FLASH_ATTN` | FA2* | fp16, bf16 | `auto`, `float16`, `bfloat16` | %16 | Any | ❌ | ✅ | ❌ | ✅ | All | ≥8.0 |
167
-| `FLASH_ATTN` | FA3* | fp16, bf16 | `auto`, `float16`, `bfloat16`, `fp8`, `fp8_e4m3`, `fp8_e5m2` | %16 | Any | ✅ | ✅ | ❌ | ✅ | All | 9.x |
168
-| `FLASH_ATTN` | FA4* | fp16, bf16 | `auto`, `float16`, `bfloat16` | %16 | Any | ✅ | ✅ | ❌ | ✅ | All | ≥10.0 |
167
+| `FLASH_ATTN` | FA3* | fp16, bf16 | `auto`, `float16`, `bfloat16`, `fp8`, `fp8_e4m3` | %16 | Any | ✅ | ✅ | ❌ | ✅ | All | 9.x |
168
+| `FLASH_ATTN` | FA4* | fp16, bf16 | `auto`, `float16`, `bfloat16`, `fp8`, `fp8_e4m3` | %16 | Any | ✅ | ✅ | ❌ | ✅ | All | ≥10.0 |
169
| `FLASH_ATTN_DIFFKV` | | fp16, bf16 | `auto` | Any | Any | ❌ | ❌ | ❌ | ✅ | Decoder | Any |
170
| `FLEX_ATTENTION` | | fp16, bf16, fp32 | `auto`, `float16`, `bfloat16` | %16 | Any | ❌ | ✅ | ✅ | ❌ | Decoder, Encoder Only | Any |
171
| `HPC_ATTN` | | fp16, bf16 | `auto`, `bfloat16`, `fp8_e4m3` | 64 | 128 | ❌ | ❌ | ❌ | ❌ | Decoder | ≥9.0 |
tests/models/quantization/test_fp8.py
+2
-8
@@ -9,8 +9,8 @@ Note: these tests will only pass on L4 GPU.
9
import pytest
10
11
from tests.quantization.utils import is_quant_method_supported
12
+from vllm.v1.attention.backends.fa_utils import flash_attn_supports_kv_cache_dtype
13
from vllm.platforms import current_platform
13
-from vllm.v1.attention.backends.fa_utils import get_flash_attn_version
14
from ..utils import check_logprobs_close
15
16
@@ -70,13 +70,7 @@ def test_models(
70
if kv_cache_dtype == "fp8_e5m2" and current_platform.is_cuda():
71
pytest.skip(f"{kv_cache_dtype} is not supported by FLASH_ATTN on CUDA.")
72
73
- if not (
74
- current_platform.is_xpu()
75
- or (
76
- get_flash_attn_version() == 3
77
- and current_platform.is_device_capability_family(90)
78
- )
79
- ):
73
+ if not flash_attn_supports_kv_cache_dtype(kv_cache_dtype):
74
pytest.skip(
75
f"{kv_cache_dtype} is not supported on this GPU type with {backend} attention."
76
)
tests/v1/attention/test_attention_backends.py
+64
-7
@@ -21,6 +21,7 @@ from vllm.platforms import current_platform
21
from vllm.utils.math_utils import cdiv
22
from vllm.utils.torch_utils import (
23
STR_DTYPE_TO_TORCH_DTYPE,
24
+ is_quantized_kv_cache,
25
is_torch_equal_or_newer,
26
set_random_seed,
27
)
@@ -45,6 +46,11 @@ BACKENDS_TO_TEST = [
46
47
DEVICE_TYPE = current_platform.device_type
48
49
+FP8_KV_CACHE_DTYPES = {
50
+ "fp8": torch.float8_e4m3fn,
51
+ "fp8_e4m3": torch.float8_e4m3fn,
52
+}
53
+
54
# Remove flashinfer from the list if it's not available
55
try:
56
import flashinfer # noqa: F401
@@ -110,6 +116,7 @@ def create_and_prepopulate_kv_cache(
116
num_blocks: int,
117
common_attn_metadata: CommonAttentionMetadata,
118
randomize_blocks: bool = True,
119
+ kv_cache_dtype: str = "auto",
120
) -> torch.Tensor:
121
"""Create and prepopulate a KV cache with context data.
122
@@ -140,8 +147,18 @@ def create_and_prepopulate_kv_cache(
147
block_table = common_attn_metadata.block_table_tensor
148
slot_mapping = common_attn_metadata.slot_mapping
149
150
+ # For an fp8 kv cache, store the cache in the fp8 dtype so that assigning
151
+ # the higher-precision context tensors quantizes them, mirroring runtime.
152
+ fp8_kv_cache = is_quantized_kv_cache(kv_cache_dtype)
153
+ storage_dtype = FP8_KV_CACHE_DTYPES[kv_cache_dtype] if fp8_kv_cache else dtype
154
+
155
kv_cache = torch.zeros(
144
- num_blocks, block_size, num_kv_heads, 2 * head_size, dtype=dtype, device=device
156
+ num_blocks,
157
+ block_size,
158
+ num_kv_heads,
159
+ 2 * head_size,
160
+ dtype=storage_dtype,
161
+ device=device,
162
)
163
kv_cache_flat = kv_cache.view(-1, num_kv_heads, 2 * head_size)
164
@@ -195,7 +212,12 @@ def create_and_prepopulate_kv_cache(
212
] * block_size + token_inter_block_offsets.to(device)
213
214
# Transpose to logical (num_blocks, num_kv_heads, block_size, 2*hs)
198
- return kv_cache.transpose(1, 2).contiguous()
215
+ kv_cache = kv_cache.transpose(1, 2).contiguous()
216
+
217
+ if fp8_kv_cache:
218
+ kv_cache = kv_cache.view(torch.uint8)
219
+
220
+ return kv_cache
221
222
223
class MockAttentionLayer:
@@ -224,6 +246,7 @@ def run_attention_backend(
246
kv_cache: torch.Tensor,
247
attn_type: AttentionType = AttentionType.DECODER,
248
sliding_window: int | None = None,
249
+ kv_cache_dtype: str = "auto",
250
) -> torch.Tensor:
251
"""Run attention computation using the specified backend's AttentionImpl."""
252
@@ -291,13 +314,16 @@ def run_attention_backend(
314
alibi_slopes=None,
315
sliding_window=sliding_window,
316
attn_type=attn_type,
294
- kv_cache_dtype="auto",
317
+ kv_cache_dtype=kv_cache_dtype,
318
)
319
320
# Create mock layer and output buffer
321
mock_layer = MockAttentionLayer(device)
322
output = torch.empty_like(query)
323
324
+ if is_quantized_kv_cache(kv_cache_dtype) and impl.supports_quant_query_input:
325
+ query = query.to(current_platform.fp8_dtype())
326
+
327
# Run forward pass
328
# NOTE: The query, key, and value are already shaped correctly
329
# in the calling test function.
@@ -324,6 +350,7 @@ def _test_backend_correctness(
350
atol: float = 1e-2,
351
rtol: float = 1e-2,
352
tensor_parallel_size: int = 1,
353
+ kv_cache_dtype: str = "auto",
354
):
355
"""
356
Test that all backends produce similar outputs to a reference implementation
@@ -372,6 +399,7 @@ def _test_backend_correctness(
399
num_gpu_blocks=8192,
400
hf_config_override=hf_config_override,
401
)
402
+ vllm_config.cache_config.cache_dtype = kv_cache_dtype
403
device = torch.device(f"{DEVICE_TYPE}:0")
404
405
kv_cache_spec = create_standard_kv_cache_spec(vllm_config, attn_type)
@@ -392,6 +420,13 @@ def _test_backend_correctness(
420
block_size = vllm_config.cache_config.block_size
421
scale = 1.0 / (head_size**0.5)
422
423
+ fp8_kv_cache = is_quantized_kv_cache(kv_cache_dtype)
424
+ if fp8_kv_cache:
425
+ query_fp8_dtype = current_platform.fp8_dtype()
426
+ kv_fp8_dtype = FP8_KV_CACHE_DTYPES[kv_cache_dtype]
427
+ atol = max(atol, 6e-2)
428
+ rtol = max(rtol, 1e-1)
429
+
430
# 2. Generate data and compute SDPA reference output
431
all_q_vllm, all_k_vllm, all_v_vllm = [], [], []
432
all_sdpa_outputs = []
@@ -407,10 +442,17 @@ def _test_backend_correctness(
442
k_full = torch.randn(s_len, num_kv_heads, head_size, dtype=dtype, device=device)
443
v_full = torch.randn(s_len, num_kv_heads, head_size, dtype=dtype, device=device)
444
445
+ if fp8_kv_cache:
446
+ q_ref = q.to(query_fp8_dtype).to(dtype)
447
+ k_ref = k_full.to(kv_fp8_dtype).to(dtype)
448
+ v_ref = v_full.to(kv_fp8_dtype).to(dtype)
449
+ else:
450
+ q_ref, k_ref, v_ref = q, k_full, v_full
451
+
452
# SDPA expects (N, H, L, D), so unsqueeze batch and permute
411
- q_sdpa_in = q.unsqueeze(0).transpose(1, 2)
412
- k_sdpa_in = k_full.unsqueeze(0).transpose(1, 2)
413
- v_sdpa_in = v_full.unsqueeze(0).transpose(1, 2)
453
+ q_sdpa_in = q_ref.unsqueeze(0).transpose(1, 2)
454
+ k_sdpa_in = k_ref.unsqueeze(0).transpose(1, 2)
455
+ v_sdpa_in = v_ref.unsqueeze(0).transpose(1, 2)
456
457
if num_q_heads != num_kv_heads:
458
assert num_q_heads % num_kv_heads == 0, (
@@ -471,6 +513,7 @@ def _test_backend_correctness(
513
num_blocks=vllm_config.cache_config.num_gpu_blocks or 1000,
514
common_attn_metadata=common_attn_metadata,
515
randomize_blocks=True,
516
+ kv_cache_dtype=kv_cache_dtype,
517
)
518
519
# 4. Run vLLM backends and compare
@@ -488,6 +531,12 @@ def _test_backend_correctness(
531
else:
532
backend_cls = None
533
534
+ if is_quantized_kv_cache(kv_cache_dtype) and (
535
+ backend_cls is None
536
+ or not backend_cls.supports_kv_cache_dtype(kv_cache_dtype)
537
+ ):
538
+ continue
539
+
540
if backend_name == AttentionBackendEnum.FLASHINFER:
541
set_kv_cache_layout("HND")
542
reset_kv_cache_layout = True
@@ -521,6 +570,7 @@ def _test_backend_correctness(
570
kv_cache_for_backend,
571
sliding_window=sliding_window,
572
attn_type=attn_type,
573
+ kv_cache_dtype=kv_cache_dtype,
574
)
575
finally:
576
if reset_kv_cache_layout:
@@ -570,8 +620,13 @@ def _test_backend_correctness(
620
)
621
@pytest.mark.parametrize("model", ["meta-llama/Meta-Llama-3-8B"])
622
@pytest.mark.parametrize("tensor_parallel_size", [1, 2, 4])
623
+@pytest.mark.parametrize("kv_cache_dtype", ["auto", "fp8", "fp8_e4m3"])
624
def test_causal_backend_correctness(
574
- default_vllm_config, batch_spec_name: str, model: str, tensor_parallel_size: int
625
+ default_vllm_config,
626
+ batch_spec_name: str,
627
+ model: str,
628
+ tensor_parallel_size: int,
629
+ kv_cache_dtype: str,
630
):
631
"""Test backend's correctness with causal attention."""
632
@@ -612,6 +667,7 @@ def test_causal_backend_correctness(
667
SMALL_BLOCK_BACKENDS,
668
causal_mask_mod,
669
tensor_parallel_size=tensor_parallel_size,
670
+ kv_cache_dtype=kv_cache_dtype,
671
)
672
673
# Fast FlexAttention needs to run with block_size=128
@@ -623,6 +679,7 @@ def test_causal_backend_correctness(
679
causal_mask_mod,
680
block_size=128,
681
tensor_parallel_size=tensor_parallel_size,
682
+ kv_cache_dtype=kv_cache_dtype,
683
)
684
685
tests/v1/attention/utils.py
+2
@@ -33,6 +33,7 @@ from vllm.v1.kv_cache_interface import (
33
EncoderOnlyAttentionSpec,
34
FullAttentionSpec,
35
MambaSpec,
36
+ get_kv_quant_mode,
37
)
38
39
@@ -178,6 +179,7 @@ def create_standard_kv_cache_spec(
179
head_size=vllm_config.model_config.get_head_size(),
180
dtype=vllm_config.model_config.dtype,
181
sliding_window=vllm_config.model_config.get_sliding_window(),
182
+ kv_quant_mode=get_kv_quant_mode(vllm_config.cache_config.cache_dtype),
183
)
184
185
tools/pre_commit/generate_attention_backend_docs.py
+66
-7
@@ -1083,7 +1083,8 @@ def parse_flash_attn_features() -> dict[str, dict[str, Any]]:
1083
return {}
1084
1085
# Analyze the functions to determine FA3/FA4-specific features
1086
- fa3_supports_fp8 = True
1086
+ fa3_supports_fp8 = False
1087
+ fa4_supports_fp8 = False
1088
fa3_supports_sinks = False
1089
fa4_supports_sinks = False
1090
fa3_compute_cap: str | None = None
@@ -1093,6 +1094,44 @@ def parse_flash_attn_features() -> dict[str, dict[str, Any]]:
1094
if not isinstance(node, ast.FunctionDef):
1095
continue
1096
1097
+ # Check flash_attn_supports_kv_cache_dtype for fp8 support per FA version.
1098
+ # Accept both equality checks and membership checks such as `in (3, 4)`.
1099
+ if node.name == "flash_attn_supports_kv_cache_dtype":
1100
+ for n in ast.walk(node):
1101
+ if not (
1102
+ isinstance(n, ast.Compare)
1103
+ and len(n.ops) == 1
1104
+ and len(n.comparators) == 1
1105
+ ):
1106
+ continue
1107
+ is_version_compare = (
1108
+ isinstance(n.left, ast.Name) and n.left.id == "fa_version"
1109
+ ) or (
1110
+ isinstance(n.left, ast.Call)
1111
+ and isinstance(n.left.func, ast.Name)
1112
+ and n.left.func.id == "get_flash_attn_version"
1113
+ )
1114
+ if not is_version_compare:
1115
+ continue
1116
+
1117
+ versions: list[Any] = []
1118
+ comparator = n.comparators[0]
1119
+ if isinstance(n.ops[0], ast.Eq) and isinstance(
1120
+ comparator, ast.Constant
1121
+ ):
1122
+ versions = [comparator.value]
1123
+ elif isinstance(n.ops[0], ast.In) and isinstance(
1124
+ comparator, (ast.Tuple, ast.List, ast.Set)
1125
+ ):
1126
+ versions = [
1127
+ elt.value
1128
+ for elt in comparator.elts
1129
+ if isinstance(elt, ast.Constant)
1130
+ ]
1131
+
1132
+ fa3_supports_fp8 |= 3 in versions
1133
+ fa4_supports_fp8 |= 4 in versions
1134
+
1135
# Check flash_attn_supports_sinks - looks for `fa_version == 3/4`
1136
# or `get_flash_attn_version() == 3/4` (also accepts `in (3, 4)`)
1137
if node.name == "flash_attn_supports_sinks":
@@ -1199,7 +1238,7 @@ def parse_flash_attn_features() -> dict[str, dict[str, Any]]:
1238
},
1239
"fa4": {
1240
"compute_capability": fa4_compute_cap,
1202
- "supports_fp8": False,
1241
+ "supports_fp8": fa4_supports_fp8,
1242
"supports_sink": fa4_supports_sinks,
1243
},
1244
}
@@ -1255,6 +1294,22 @@ def parse_flashinfer_trtllm_features() -> dict[str, dict[str, Any]]:
1294
# ---------------------------------------------------------------------------
1295
1296
1297
+def _apply_fp8_support(kv_cache_dtypes: str, supports_fp8: bool) -> str:
1298
+ """Add or remove fp8 dtypes from a comma-separated kv_cache_dtypes string.
1299
+
1300
+ The base FLASH_ATTN backend lists fp8 dtypes in ``supported_kv_cache_dtypes``,
1301
+ but actual support varies by FA version (e.g. FA2 has no fp8 support), so each
1302
+ variant's row is normalized to match its own capability.
1303
+ """
1304
+ fp8_dtypes = {"fp8", "fp8_e4m3", "fp8_e5m2"}
1305
+ dtypes = kv_cache_dtypes.split(", ")
1306
+ base_dtypes = [dtype for dtype in dtypes if dtype not in fp8_dtypes]
1307
+ if supports_fp8:
1308
+ advertised_fp8_dtypes = [dtype for dtype in dtypes if dtype in fp8_dtypes]
1309
+ return ", ".join(base_dtypes + advertised_fp8_dtypes)
1310
+ return ", ".join(base_dtypes)
1311
+
1312
+
1313
def _expand_flash_attn_variants(
1314
all_backends: list[dict[str, Any]],
1315
fa_features: dict[str, dict[str, Any]],
@@ -1275,6 +1330,9 @@ def _expand_flash_attn_variants(
1330
fa2["_sort_key"] = "FLASH_ATTN"
1331
fa2["_sort_order"] = 0
1332
fa2["supports_sink"] = fa_features["fa2"]["supports_sink"]
1333
+ fa2["kv_cache_dtypes"] = _apply_fp8_support(
1334
+ backend["kv_cache_dtypes"], fa_features["fa2"]["supports_fp8"]
1335
+ )
1336
1337
# Create FA3 entry (uses parsed compute_capability from fa_utils)
1338
fa3 = backend.copy()
@@ -1284,11 +1342,9 @@ def _expand_flash_attn_variants(
1342
if fa_features["fa3"]["compute_capability"]:
1343
fa3["compute_capability"] = fa_features["fa3"]["compute_capability"]
1344
fa3["supports_sink"] = fa_features["fa3"]["supports_sink"]
1287
- if fa_features["fa3"]["supports_fp8"]:
1288
- base_dtypes = backend["kv_cache_dtypes"].split(", ")
1289
- fp8_dtypes = ["fp8", "fp8_e4m3", "fp8_e5m2"]
1290
- new_dtypes = [d for d in fp8_dtypes if d not in base_dtypes]
1291
- fa3["kv_cache_dtypes"] = ", ".join(base_dtypes + new_dtypes)
1345
+ fa3["kv_cache_dtypes"] = _apply_fp8_support(
1346
+ backend["kv_cache_dtypes"], fa_features["fa3"]["supports_fp8"]
1347
+ )
1348
1349
expanded.append(fa2)
1350
expanded.append(fa3)
@@ -1302,6 +1358,9 @@ def _expand_flash_attn_variants(
1358
if fa_features["fa4"].get("compute_capability"):
1359
fa4["compute_capability"] = fa_features["fa4"]["compute_capability"]
1360
fa4["supports_sink"] = fa_features["fa4"]["supports_sink"]
1361
+ fa4["kv_cache_dtypes"] = _apply_fp8_support(
1362
+ backend["kv_cache_dtypes"], fa_features["fa4"]["supports_fp8"]
1363
+ )
1364
expanded.append(fa4)
1365
1366
return expanded
vllm/v1/attention/backends/fa_utils.py
+23
@@ -273,6 +273,29 @@ def is_fa_version_supported(fa_version: int) -> bool:
273
return False
274
275
276
+def flash_attn_supports_kv_cache_dtype(
277
+ kv_cache_dtype: str = "fp8_e4m3",
278
+ *,
279
+ requires_alibi: bool = False,
280
+ head_size: int | None = None,
281
+ head_size_v: int | None = None,
282
+ has_sinks: bool = False,
283
+) -> bool:
284
+ if kv_cache_dtype == "fp8_e5m2":
285
+ return False
286
+ if current_platform.is_xpu():
287
+ return True
288
+ fa_version = get_flash_attn_version(
289
+ requires_alibi=requires_alibi,
290
+ head_size=head_size,
291
+ head_size_v=head_size_v,
292
+ has_sinks=has_sinks,
293
+ )
294
+ return (fa_version == 3 and current_platform.is_device_capability_family(90)) or (
295
+ fa_version == 4 and current_platform.is_device_capability_family(100)
296
+ )
297
+
298
+
299
def flash_attn_supports_quant_query_input() -> bool:
300
return not current_platform.is_xpu()
301
vllm/v1/attention/backends/flash_attn.py
+34
-8
@@ -22,6 +22,7 @@ from vllm.v1.attention.backend import (
22
MultipleOf,
23
)
24
from vllm.v1.attention.backends.fa_utils import (
25
+ flash_attn_supports_kv_cache_dtype,
26
flash_attn_supports_quant_query_input,
27
get_flash_attn_version,
28
is_fa_version_supported,
@@ -74,6 +75,8 @@ class FlashAttentionBackend(AttentionBackend):
75
"auto",
76
"float16",
77
"bfloat16",
78
+ "fp8",
79
+ "fp8_e4m3",
80
]
81
82
@staticmethod
@@ -178,14 +181,11 @@ class FlashAttentionBackend(AttentionBackend):
181
def supports_kv_cache_dtype(cls, kv_cache_dtype: CacheDType | None) -> bool:
182
if kv_cache_dtype is None:
183
return True
181
- if kv_cache_dtype in ("fp8", "fp8_e4m3"):
182
- if current_platform.is_xpu():
183
- return True
184
- return (
185
- get_flash_attn_version() == 3
186
- and current_platform.is_device_capability_family(90)
187
- )
188
- return kv_cache_dtype in ["auto", "float16", "bfloat16"]
184
+ if kv_cache_dtype not in cls.supported_kv_cache_dtypes:
185
+ return False
186
+ if is_quantized_kv_cache(kv_cache_dtype):
187
+ return flash_attn_supports_kv_cache_dtype(kv_cache_dtype)
188
+ return True
189
190
@classmethod
191
def supports_mm_prefix(cls) -> bool:
@@ -216,6 +216,17 @@ class FlashAttentionBackend(AttentionBackend):
216
) -> str | None:
217
if has_sink and device_capability < DeviceCapability(9, 0):
218
return "sink not supported on compute capability < 9.0"
219
+ if (
220
+ kv_cache_dtype is not None
221
+ and is_quantized_kv_cache(kv_cache_dtype)
222
+ and not flash_attn_supports_kv_cache_dtype(
223
+ kv_cache_dtype,
224
+ head_size=head_size,
225
+ head_size_v=head_size,
226
+ has_sinks=has_sink,
227
+ )
228
+ ):
229
+ return "FP8 KV cache requires FA3 on SM90 or FA4 on SM100"
230
if (
231
use_mm_prefix
232
and get_flash_attn_version(head_size=head_size, has_sinks=has_sink) != 4
@@ -772,6 +783,7 @@ class FlashAttentionImpl(AttentionImpl):
783
self.vllm_flash_attn_version = get_flash_attn_version(
784
requires_alibi=alibi_slopes is not None,
785
head_size=head_size,
786
+ has_sinks=sinks is not None,
787
)
788
logger.info_once(
789
"Using FlashAttention version %s",
@@ -780,6 +792,20 @@ class FlashAttentionImpl(AttentionImpl):
792
# Cache the batch invariant result for use in forward passes
793
self.batch_invariant_enabled = envs.VLLM_BATCH_INVARIANT
794
795
+ if is_quantized_kv_cache(
796
+ self.kv_cache_dtype
797
+ ) and not flash_attn_supports_kv_cache_dtype(
798
+ self.kv_cache_dtype,
799
+ requires_alibi=alibi_slopes is not None,
800
+ head_size=head_size,
801
+ head_size_v=head_size,
802
+ has_sinks=sinks is not None,
803
+ ):
804
+ raise NotImplementedError(
805
+ f"FlashAttention does not support {self.kv_cache_dtype}"
806
+ " kv-cache on this device."
807
+ )
808
+
809
self.sinks = sinks
810
if self.sinks is not None:
811
assert flash_attn_supports_sinks(), (