main
py 375 lines 13.4 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from typing import Any
5
6 import vllm.envs as envs
7 from vllm.logger import init_logger
8 from vllm.platforms import current_platform
9 from vllm.utils.torch_utils import is_quantized_kv_cache
10
11 logger = init_logger(__name__)
12
13 # The dedicated hd256 kernel requires one 128-token page per tile.
14 FA4_HD256_PAGE_SIZE = 128
15
16 # Track whether upstream flash-attn is available on ROCm.
17 # Set during module initialization and never modified afterwards.
18 # This module-level flag avoids repeated import attempts and ensures
19 # consistent behavior (similar to IS_AITER_FOUND in _aiter_ops.py).
20 _ROCM_FLASH_ATTN_AVAILABLE = False
21
22 if current_platform.is_cuda():
23 from vllm._custom_ops import reshape_and_cache_flash
24 from vllm.vllm_flash_attn import ( # type: ignore[attr-defined]
25 compile_flash_attn_varlen_func_from_specs,
26 flash_attn_varlen_func,
27 get_scheduler_metadata,
28 )
29
30 elif current_platform.is_xpu():
31 from vllm import _custom_ops as ops
32 from vllm._xpu_ops import xpu_ops
33
34 reshape_and_cache_flash = ops.reshape_and_cache_flash
35 flash_attn_varlen_func = xpu_ops.flash_attn_varlen_func # type: ignore[assignment]
36 compile_flash_attn_varlen_func_from_specs = None # type: ignore[assignment]
37 get_scheduler_metadata = xpu_ops.get_scheduler_metadata # type: ignore[assignment]
38 elif current_platform.is_rocm():
39 # On ROCm we use AITER's Triton flash-attention; the upstream flash-attn
40 # package is not installed/available. (Same source as aiter_triton_mla.py.)
41 # The FA4 compile-from-specs API is CUDA-only, so it is unavailable on ROCm
42 # regardless of whether AITER is present.
43 from vllm.platforms.rocm import on_gfx1250
44
45 compile_flash_attn_varlen_func_from_specs = None # type: ignore[assignment]
46 try:
47 if on_gfx1250():
48 from aiter.ops.triton.mha import ( # type: ignore[no-redef]
49 flash_attn_varlen_func,
50 )
51 else:
52 from flash_attn import flash_attn_varlen_func # type: ignore[no-redef]
53
54 _ROCM_FLASH_ATTN_AVAILABLE = True
55 except ImportError:
56
57 def flash_attn_varlen_func(*args: Any, **kwargs: Any) -> Any: # type: ignore[no-redef,misc]
58 package = "aiter" if on_gfx1250() else "flash-attn"
59 raise ImportError(
60 f"ROCm platform requires upstream {package} "
61 f"to be installed. Please install {package} first."
62 )
63
64 # ROCm doesn't use scheduler metadata (FA3 feature), provide stub
65 def get_scheduler_metadata(*args: Any, **kwargs: Any) -> None: # type: ignore[misc]
66 return None
67
68 # ROCm uses the C++ custom op for reshape_and_cache
69 from vllm import _custom_ops as ops
70
71 reshape_and_cache_flash = ops.reshape_and_cache_flash
72
73
74 def get_flash_attn_version(
75 requires_alibi: bool = False,
76 head_size: int | None = None,
77 head_size_v: int | None = None,
78 has_sinks: bool = False,
79 requires_softcap: bool = False,
80 kv_cache_block_size: int | None = None,
81 supports_fa4_hd256: bool = False,
82 ) -> int | None:
83 if current_platform.is_xpu():
84 return 2
85 if current_platform.is_rocm():
86 # ROCm doesn't use vllm_flash_attn; return None to skip fa_version arg
87 return None
88 try:
89 from vllm.vllm_flash_attn.flash_attn_interface import (
90 fa_version_unsupported_reason,
91 is_fa_version_supported,
92 )
93
94 device_capability = current_platform.get_device_capability()
95
96 assert device_capability is not None
97
98 # 1. default version depending on platform
99 if device_capability.major == 9 and is_fa_version_supported(3):
100 # Hopper (SM90): prefer FA3
101 fa_version = 3
102 elif device_capability.major == 10 and is_fa_version_supported(4):
103 # Blackwell (SM100+, restrict to SM100 for now): prefer FA4
104 fa_version = 4
105 else:
106 # Fallback to FA2
107 fa_version = 2
108
109 # 2. override if passed by environment or config
110 from vllm.config import get_current_vllm_config_or_none
111
112 vllm_config = get_current_vllm_config_or_none()
113 if (
114 vllm_config is not None
115 and vllm_config.attention_config.flash_attn_version is not None
116 ):
117 fa_version = vllm_config.attention_config.flash_attn_version
118
119 # 3. fallback for unsupported combinations
120 if device_capability.major >= 10 and fa_version == 3:
121 logger.warning_once(
122 "Cannot use FA version 3 on Blackwell platform, "
123 "defaulting to FA version 4 if supported, otherwise FA2."
124 )
125 fa_version = 4 if is_fa_version_supported(4) else 2
126
127 if requires_alibi and fa_version == 3:
128 logger.warning_once(
129 "Cannot use FA version 3 with ALiBi, defaulting to FA version 2."
130 )
131 fa_version = 2
132
133 if requires_alibi and fa_version == 4:
134 logger.warning_once(
135 "Cannot use FA version 4 with ALiBi, defaulting to FA version 2."
136 )
137 fa_version = 2
138
139 # Some FA3 unsupported SM90 cases can use FA4 when available.
140 if (
141 fa_version == 3
142 and device_capability.major == 9
143 and is_fa_version_supported(4)
144 ):
145 upgrade_reason = None
146 if head_size is not None and head_size > 256:
147 upgrade_reason = f"FA3 does not support head_size={head_size} on SM90"
148 elif (
149 has_sinks
150 and head_size is not None
151 and head_size_v is not None
152 and head_size != head_size_v
153 ):
154 upgrade_reason = "Diff-KV with sinks"
155 elif (
156 vllm_config is not None
157 and vllm_config.model_config is not None
158 and vllm_config.model_config.is_diffusion
159 ):
160 upgrade_reason = "Per-sequence causal (dynamic_causal) requires FA4"
161 if upgrade_reason:
162 logger.info_once(
163 "%s: upgrading FlashAttention 3 -> 4",
164 upgrade_reason,
165 scope="local",
166 )
167 fa_version = 4
168
169 # FA4 currently uses batch-shape-dependent scheduling
170 # heuristics on SM100+, which breaks batch invariance.
171 if envs.VLLM_BATCH_INVARIANT and fa_version == 4:
172 logger.warning_once(
173 "Cannot use FA version 4 with batch invariance, "
174 "defaulting to FA version 2.",
175 )
176 fa_version = 2
177
178 if fa_version == 4 and uses_fa4_hd256_kernel(head_size, head_size_v):
179 if not supports_fa4_hd256:
180 fa_version = 2
181 elif (
182 reason := _fa4_hd256_fallback_reason(
183 has_sinks, requires_softcap, kv_cache_block_size, vllm_config
184 )
185 ) is not None:
186 logger.warning_once(
187 "FA4's Blackwell head_size=256 kernel does not support %s, "
188 "defaulting to FA version 2.",
189 reason,
190 )
191 fa_version = 2
192
193 # FA4 head dimensions on Blackwell are limited by TMEM capacity.
194 if (
195 fa_version == 4
196 and device_capability.major >= 10
197 and head_size is not None
198 and head_size > 128
199 and not (
200 (head_size == 256 and head_size_v in (None, 256))
201 or (head_size == 192 and head_size_v == 128)
202 )
203 ):
204 logger.warning_once(
205 "FA4 on Blackwell does not support head_size=%d due to TMEM "
206 "capacity limits, defaulting to FA version 2.",
207 head_size,
208 )
209 fa_version = 2
210
211 if not is_fa_version_supported(fa_version):
212 logger.error(
213 "Cannot use FA version %d is not supported due to %s",
214 fa_version,
215 fa_version_unsupported_reason(fa_version),
216 )
217
218 assert is_fa_version_supported(fa_version)
219 return fa_version
220 except (ImportError, AssertionError):
221 return None
222
223
224 def uses_fa4_hd256_kernel(
225 head_size: int | None, head_size_v: int | None = None
226 ) -> bool:
227 """Return whether FA4 uses its dedicated hd256 kernel."""
228 if head_size != 256:
229 return False
230 if head_size_v is not None and head_size_v != 256:
231 return False
232 capability = current_platform.get_device_capability()
233 return capability is not None and capability.major in (10, 11)
234
235
236 def _fa4_hd256_fallback_reason(
237 has_sinks: bool,
238 requires_softcap: bool,
239 kv_cache_block_size: int | None,
240 vllm_config: Any,
241 ) -> str | None:
242 model_config = vllm_config.model_config if vllm_config is not None else None
243 cache_config = vllm_config.cache_config if vllm_config is not None else None
244 if has_sinks:
245 return "attention sinks"
246 if requires_softcap or (
247 # Keep model-level and per-layer version selection consistent.
248 model_config is not None
249 and getattr(model_config.hf_text_config, "attn_logit_softcapping", None)
250 ):
251 return "logits soft capping"
252 if cache_config is not None and is_quantized_kv_cache(cache_config.cache_dtype):
253 return f"quantized KV cache dtype {cache_config.cache_dtype}"
254 if kv_cache_block_size is not None and kv_cache_block_size % FA4_HD256_PAGE_SIZE:
255 # Larger blocks are split into 128-token kernel pages.
256 return f"a KV cache block size of {kv_cache_block_size}"
257 if model_config is not None:
258 if model_config.is_mm_prefix_lm:
259 return "mm_prefix bidirectional attention"
260 if model_config.rswa_window is not None:
261 return "R-SWA"
262 if (
263 vllm_config is not None
264 and vllm_config.parallel_config.decode_context_parallel_size > 1
265 ):
266 # DCP reuses an unsliced block table with a non-page-aligned length.
267 return "decode context parallelism"
268 return None
269
270
271 def is_fa_version_supported(fa_version: int) -> bool:
272 try:
273 from vllm.vllm_flash_attn.flash_attn_interface import (
274 is_fa_version_supported as _is_fa_version_supported,
275 )
276
277 return _is_fa_version_supported(fa_version)
278 except ImportError:
279 return False
280
281
282 def flash_attn_supports_kv_cache_dtype(
283 kv_cache_dtype: str = "fp8_e4m3",
284 *,
285 requires_alibi: bool = False,
286 head_size: int | None = None,
287 head_size_v: int | None = None,
288 has_sinks: bool = False,
289 requires_softcap: bool = False,
290 kv_cache_block_size: int | None = None,
291 supports_fa4_hd256: bool = False,
292 ) -> bool:
293 if kv_cache_dtype == "fp8_e5m2":
294 return False
295 if current_platform.is_xpu():
296 return True
297 fa_version = get_flash_attn_version(
298 requires_alibi=requires_alibi,
299 head_size=head_size,
300 head_size_v=head_size_v,
301 has_sinks=has_sinks,
302 requires_softcap=requires_softcap,
303 kv_cache_block_size=kv_cache_block_size,
304 supports_fa4_hd256=supports_fa4_hd256,
305 )
306 is_sm90 = current_platform.is_device_capability_family(90)
307 sm90_fp8_kv_supported = fa_version == 3 or (
308 fa_version == 4 and head_size in (None, 512)
309 )
310 return (sm90_fp8_kv_supported and is_sm90) or (
311 fa_version == 4 and current_platform.is_device_capability_family(100)
312 )
313
314
315 def flash_attn_supports_quant_query_input() -> bool:
316 return not current_platform.is_xpu()
317
318
319 def flash_attn_supports_sinks() -> bool:
320 if current_platform.is_xpu():
321 return True
322 return get_flash_attn_version() in (3, 4)
323
324
325 def flash_attn_supports_mla():
326 from vllm.platforms import current_platform
327
328 if current_platform.is_cuda():
329 try:
330 from vllm.vllm_flash_attn.flash_attn_interface import (
331 is_fa_version_supported,
332 )
333
334 return is_fa_version_supported(
335 3
336 ) and current_platform.is_device_capability_family(90)
337
338 # NOTE(Lucas): FA4 CuteDSL does NOT currently support MLA's non-standard
339 # head dimensions (576 for qk, 512 for v) due to TMEM capacity limits.
340
341 except (ImportError, AssertionError):
342 pass
343 return False
344
345
346 def is_flash_attn_varlen_func_available() -> bool:
347 """Check if flash_attn_varlen_func is available.
348
349 This function determines whether the flash_attn_varlen_func imported at module
350 level is a working implementation or a stub.
351
352 Platform-specific sources:
353 - CUDA: vllm.vllm_flash_attn.flash_attn_varlen_func
354 - XPU: xpu_ops.flash_attn_varlen_func
355 - ROCm: aiter.ops.triton.mha.flash_attn_varlen_func (if AITER available) or
356 upstream flash_attn.flash_attn_varlen_func
357
358 Note: This is separate from the AITER flash attention backend (rocm_aiter_fa.py)
359 which uses rocm_aiter_ops.flash_attn_varlen_func. The condition to use AITER is
360 handled separately via _aiter_ops.is_aiter_found_and_supported().
361
362 Returns:
363 bool: True if a working flash_attn_varlen_func implementation is available.
364
365 """
366 if current_platform.is_cuda() or current_platform.is_xpu():
367 # CUDA and XPU always have flash_attn_varlen_func available
368 return True
369
370 if current_platform.is_rocm():
371 # Use the flag set during module import to check if
372 # upstream flash-attn was successfully imported
373 return _ROCM_FLASH_ATTN_AVAILABLE
374
375 return False