+1








4f1da84eb5
Signed-off-by: Douglas Lehr <Doug.Lehr@amd.com> Signed-off-by: jpvillam <juan.villamizar@amd.com> Signed-off-by: Daniel <danichan@amd.com> Signed-off-by: Jaden Mathias <jaden.mathias@amd.com> Signed-off-by: jpvillam <jpvillam@amd.com> Signed-off-by: Juan Villamizar <100237675+jpvillam-amd@users.noreply.github.com> Signed-off-by: Douglas Lehr <91553416+dllehr-amd@users.noreply.github.com> Signed-off-by: Jaden Mathias <Jaden.Mathias@amd.com> Co-authored-by: Douglas Lehr <Doug.Lehr@amd.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Daniel <danichan@amd.com> Co-authored-by: Jaden Mathias <jaden.mathias@amd.com> Co-authored-by: jpvillam <jpvillam@amd.com> Co-authored-by: root <root@ctheliosr-1b114-f01-2.mnb.dcgpu> Co-authored-by: Douglas Lehr <91553416+dllehr-amd@users.noreply.github.com> Co-authored-by: dllehr <douglehr@amd.com> Co-authored-by: Daniel Chan <Daniel.Chan@amd.com> Co-authored-by: Andreas Karatzas <akaratza@amd.com>
379 lines
13 KiB
Python
379 lines
13 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
import torch
|
|
|
|
import vllm.envs as envs
|
|
from vllm.logger import init_logger
|
|
from vllm.platforms import current_platform
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
# Track whether upstream flash-attn is available on ROCm.
|
|
# Set during module initialization and never modified afterwards.
|
|
# This module-level flag avoids repeated import attempts and ensures
|
|
# consistent behavior (similar to IS_AITER_FOUND in _aiter_ops.py).
|
|
_ROCM_FLASH_ATTN_AVAILABLE = False
|
|
|
|
if current_platform.is_cuda():
|
|
from vllm._custom_ops import reshape_and_cache_flash
|
|
from vllm.vllm_flash_attn import ( # type: ignore[attr-defined]
|
|
compile_flash_attn_varlen_func_from_specs,
|
|
flash_attn_varlen_func,
|
|
get_scheduler_metadata,
|
|
)
|
|
|
|
elif current_platform.is_xpu():
|
|
from vllm import _custom_ops as ops
|
|
from vllm._xpu_ops import xpu_ops
|
|
|
|
reshape_and_cache_flash = ops.reshape_and_cache_flash
|
|
flash_attn_varlen_func = xpu_ops.flash_attn_varlen_func # type: ignore[assignment]
|
|
compile_flash_attn_varlen_func_from_specs = None # type: ignore[assignment]
|
|
get_scheduler_metadata = xpu_ops.get_scheduler_metadata # type: ignore[assignment]
|
|
elif current_platform.is_rocm():
|
|
# On ROCm we use AITER's Triton flash-attention; the upstream flash-attn
|
|
# package is not installed/available. (Same source as aiter_triton_mla.py.)
|
|
# The FA4 compile-from-specs API is CUDA-only, so it is unavailable on ROCm
|
|
# regardless of whether AITER is present.
|
|
from vllm.platforms.rocm import on_gfx1250
|
|
|
|
compile_flash_attn_varlen_func_from_specs = None # type: ignore[assignment]
|
|
try:
|
|
if on_gfx1250():
|
|
from aiter.ops.triton.mha import ( # type: ignore[no-redef]
|
|
flash_attn_varlen_func,
|
|
)
|
|
else:
|
|
from flash_attn import flash_attn_varlen_func # type: ignore[no-redef]
|
|
|
|
_ROCM_FLASH_ATTN_AVAILABLE = True
|
|
except ImportError:
|
|
|
|
def flash_attn_varlen_func(*args: Any, **kwargs: Any) -> Any: # type: ignore[no-redef,misc]
|
|
package = "aiter" if on_gfx1250() else "flash-attn"
|
|
raise ImportError(
|
|
f"ROCm platform requires upstream {package} "
|
|
f"to be installed. Please install {package} first."
|
|
)
|
|
|
|
# ROCm doesn't use scheduler metadata (FA3 feature), provide stub
|
|
def get_scheduler_metadata(*args: Any, **kwargs: Any) -> None: # type: ignore[misc]
|
|
return None
|
|
|
|
# ROCm uses the C++ custom op for reshape_and_cache
|
|
from vllm import _custom_ops as ops
|
|
|
|
reshape_and_cache_flash = ops.reshape_and_cache_flash
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FlashAttentionCuTeDSLCompileSpec:
|
|
"""High-level FA4 compile-only request used by vLLM warmup.
|
|
|
|
This is not the CuTeDSL cache key. FA4 owns the selector that maps these
|
|
serving inputs to the actual compile-static fields: tile sizes, q_stage,
|
|
Split-KV, scheduler choice, layout-presence booleans, dtype/head dims,
|
|
arch, and related fields.
|
|
"""
|
|
|
|
q_shape: tuple[int, ...]
|
|
k_shape: tuple[int, ...]
|
|
v_shape: tuple[int, ...]
|
|
q_dtype: torch.dtype
|
|
max_seqlen_q: int
|
|
max_seqlen_k: int
|
|
softmax_scale: float
|
|
causal: bool
|
|
fa_version: int
|
|
v_stride: tuple[int, ...] | None = None
|
|
cu_seqlens_q_shape: tuple[int, ...] | None = None
|
|
cu_seqlens_k_shape: tuple[int, ...] | None = None
|
|
window_size: tuple[int, int] | None = None
|
|
return_softmax_lse: bool = False
|
|
num_splits: int = 0
|
|
|
|
def compile(self) -> None:
|
|
assert compile_flash_attn_varlen_func_from_specs is not None
|
|
window_size = list(self.window_size) if self.window_size is not None else None
|
|
compile_flash_attn_varlen_func_from_specs(
|
|
q_shape=self.q_shape,
|
|
k_shape=self.k_shape,
|
|
v_shape=self.v_shape,
|
|
q_dtype=self.q_dtype,
|
|
v_stride=self.v_stride,
|
|
cu_seqlens_q_shape=self.cu_seqlens_q_shape,
|
|
cu_seqlens_k_shape=self.cu_seqlens_k_shape,
|
|
max_seqlen_q=self.max_seqlen_q,
|
|
max_seqlen_k=self.max_seqlen_k,
|
|
softmax_scale=self.softmax_scale,
|
|
causal=self.causal,
|
|
window_size=window_size,
|
|
return_softmax_lse=self.return_softmax_lse,
|
|
fa_version=self.fa_version,
|
|
num_splits=self.num_splits,
|
|
)
|
|
|
|
def request_key(self) -> tuple[object, ...]:
|
|
return (
|
|
self.q_shape,
|
|
self.k_shape,
|
|
self.v_shape,
|
|
self.q_dtype,
|
|
self.max_seqlen_q,
|
|
self.max_seqlen_k,
|
|
self.softmax_scale,
|
|
self.causal,
|
|
self.fa_version,
|
|
self.v_stride,
|
|
self.cu_seqlens_q_shape,
|
|
self.cu_seqlens_k_shape,
|
|
self.window_size,
|
|
self.return_softmax_lse,
|
|
self.num_splits,
|
|
)
|
|
|
|
|
|
def get_flash_attn_version(
|
|
requires_alibi: bool = False,
|
|
head_size: int | None = None,
|
|
head_size_v: int | None = None,
|
|
has_sinks: bool = False,
|
|
requires_local_attention: bool = False,
|
|
) -> int | None:
|
|
if current_platform.is_xpu():
|
|
return 2
|
|
if current_platform.is_rocm():
|
|
# ROCm doesn't use vllm_flash_attn; return None to skip fa_version arg
|
|
return None
|
|
try:
|
|
from vllm.vllm_flash_attn.flash_attn_interface import (
|
|
fa_version_unsupported_reason,
|
|
is_fa_version_supported,
|
|
)
|
|
|
|
device_capability = current_platform.get_device_capability()
|
|
|
|
assert device_capability is not None
|
|
|
|
# 1. default version depending on platform
|
|
if device_capability.major == 9 and is_fa_version_supported(3):
|
|
# Hopper (SM90): prefer FA3
|
|
fa_version = 3
|
|
elif device_capability.major == 10 and is_fa_version_supported(4):
|
|
# Blackwell (SM100+, restrict to SM100 for now): prefer FA4
|
|
fa_version = 4
|
|
else:
|
|
# Fallback to FA2
|
|
fa_version = 2
|
|
|
|
# 2. override if passed by environment or config
|
|
from vllm.config import get_current_vllm_config_or_none
|
|
|
|
vllm_config = get_current_vllm_config_or_none()
|
|
if (
|
|
vllm_config is not None
|
|
and vllm_config.attention_config.flash_attn_version is not None
|
|
):
|
|
fa_version = vllm_config.attention_config.flash_attn_version
|
|
|
|
# 3. fallback for unsupported combinations
|
|
if device_capability.major >= 10 and fa_version == 3:
|
|
logger.warning_once(
|
|
"Cannot use FA version 3 on Blackwell platform, "
|
|
"defaulting to FA version 4 if supported, otherwise FA2."
|
|
)
|
|
fa_version = 4 if is_fa_version_supported(4) else 2
|
|
|
|
if requires_alibi and fa_version == 3:
|
|
logger.warning_once(
|
|
"Cannot use FA version 3 with ALiBi, defaulting to FA version 2."
|
|
)
|
|
fa_version = 2
|
|
|
|
if requires_alibi and fa_version == 4:
|
|
logger.warning_once(
|
|
"Cannot use FA version 4 with ALiBi, defaulting to FA version 2."
|
|
)
|
|
fa_version = 2
|
|
|
|
# Some FA3 unsupported SM90 cases can use FA4 when available.
|
|
if (
|
|
fa_version == 3
|
|
and device_capability.major == 9
|
|
and is_fa_version_supported(4)
|
|
):
|
|
upgrade_reason = None
|
|
if head_size is not None and head_size > 256:
|
|
upgrade_reason = f"FA3 does not support head_size={head_size} on SM90"
|
|
elif (
|
|
has_sinks
|
|
and head_size is not None
|
|
and head_size_v is not None
|
|
and head_size != head_size_v
|
|
):
|
|
upgrade_reason = "Diff-KV with sinks"
|
|
elif (
|
|
vllm_config is not None
|
|
and vllm_config.model_config is not None
|
|
and vllm_config.model_config.is_diffusion
|
|
):
|
|
upgrade_reason = "Per-sequence causal (dynamic_causal) requires FA4"
|
|
if upgrade_reason:
|
|
logger.info_once(
|
|
"%s: upgrading FlashAttention 3 -> 4",
|
|
upgrade_reason,
|
|
scope="local",
|
|
)
|
|
fa_version = 4
|
|
|
|
# FA4 currently uses batch-shape-dependent scheduling
|
|
# heuristics on SM100+, which breaks batch invariance.
|
|
if envs.VLLM_BATCH_INVARIANT and fa_version == 4:
|
|
logger.warning_once(
|
|
"Cannot use FA version 4 with batch invariance, "
|
|
"defaulting to FA version 2.",
|
|
)
|
|
fa_version = 2
|
|
|
|
if (
|
|
fa_version == 4
|
|
and device_capability.major >= 10
|
|
and head_size == 256
|
|
and requires_local_attention
|
|
):
|
|
logger.warning_once(
|
|
"FA4 on Blackwell does not support local attention with "
|
|
"head_size=256, defaulting to FA version 2."
|
|
)
|
|
fa_version = 2
|
|
|
|
# FA4 on SM100 (Blackwell) has TMEM capacity limits that restrict
|
|
# supported head dimensions to ≤128, with exceptions for 256 and 192/128 (MLA
|
|
# prefill). Development of symmetric 192, 384, and 512 support is being tracked
|
|
# in https://github.com/Dao-AILab/flash-attention/issues/2456
|
|
if (
|
|
fa_version == 4
|
|
and device_capability.major >= 10
|
|
and head_size is not None
|
|
and head_size > 128
|
|
and not (head_size == 256 or (head_size == 192 and head_size_v == 128))
|
|
):
|
|
logger.warning_once(
|
|
"FA4 on Blackwell does not support head_size=%d due to TMEM "
|
|
"capacity limits, defaulting to FA version 2.",
|
|
head_size,
|
|
)
|
|
fa_version = 2
|
|
|
|
if not is_fa_version_supported(fa_version):
|
|
logger.error(
|
|
"Cannot use FA version %d is not supported due to %s",
|
|
fa_version,
|
|
fa_version_unsupported_reason(fa_version),
|
|
)
|
|
|
|
assert is_fa_version_supported(fa_version)
|
|
return fa_version
|
|
except (ImportError, AssertionError):
|
|
return None
|
|
|
|
|
|
def is_fa_version_supported(fa_version: int) -> bool:
|
|
try:
|
|
from vllm.vllm_flash_attn.flash_attn_interface import (
|
|
is_fa_version_supported as _is_fa_version_supported,
|
|
)
|
|
|
|
return _is_fa_version_supported(fa_version)
|
|
except ImportError:
|
|
return False
|
|
|
|
|
|
def flash_attn_supports_kv_cache_dtype(
|
|
kv_cache_dtype: str = "fp8_e4m3",
|
|
*,
|
|
requires_alibi: bool = False,
|
|
head_size: int | None = None,
|
|
head_size_v: int | None = None,
|
|
has_sinks: bool = False,
|
|
) -> bool:
|
|
if kv_cache_dtype == "fp8_e5m2":
|
|
return False
|
|
if current_platform.is_xpu():
|
|
return True
|
|
fa_version = get_flash_attn_version(
|
|
requires_alibi=requires_alibi,
|
|
head_size=head_size,
|
|
head_size_v=head_size_v,
|
|
has_sinks=has_sinks,
|
|
)
|
|
return (fa_version == 3 and current_platform.is_device_capability_family(90)) or (
|
|
fa_version == 4 and current_platform.is_device_capability_family(100)
|
|
)
|
|
|
|
|
|
def flash_attn_supports_quant_query_input() -> bool:
|
|
return not current_platform.is_xpu()
|
|
|
|
|
|
def flash_attn_supports_sinks() -> bool:
|
|
if current_platform.is_xpu():
|
|
return True
|
|
return get_flash_attn_version() in (3, 4)
|
|
|
|
|
|
def flash_attn_supports_mla():
|
|
from vllm.platforms import current_platform
|
|
|
|
if current_platform.is_cuda():
|
|
try:
|
|
from vllm.vllm_flash_attn.flash_attn_interface import (
|
|
is_fa_version_supported,
|
|
)
|
|
|
|
return is_fa_version_supported(
|
|
3
|
|
) and current_platform.is_device_capability_family(90)
|
|
|
|
# NOTE(Lucas): FA4 CuteDSL does NOT currently support MLA's non-standard
|
|
# head dimensions (576 for qk, 512 for v) due to TMEM capacity limits.
|
|
|
|
except (ImportError, AssertionError):
|
|
pass
|
|
return False
|
|
|
|
|
|
def is_flash_attn_varlen_func_available() -> bool:
|
|
"""Check if flash_attn_varlen_func is available.
|
|
|
|
This function determines whether the flash_attn_varlen_func imported at module
|
|
level is a working implementation or a stub.
|
|
|
|
Platform-specific sources:
|
|
- CUDA: vllm.vllm_flash_attn.flash_attn_varlen_func
|
|
- XPU: xpu_ops.flash_attn_varlen_func
|
|
- ROCm: aiter.ops.triton.mha.flash_attn_varlen_func (if AITER available) or
|
|
upstream flash_attn.flash_attn_varlen_func
|
|
|
|
Note: This is separate from the AITER flash attention backend (rocm_aiter_fa.py)
|
|
which uses rocm_aiter_ops.flash_attn_varlen_func. The condition to use AITER is
|
|
handled separately via _aiter_ops.is_aiter_found_and_supported().
|
|
|
|
Returns:
|
|
bool: True if a working flash_attn_varlen_func implementation is available.
|
|
"""
|
|
if current_platform.is_cuda() or current_platform.is_xpu():
|
|
# CUDA and XPU always have flash_attn_varlen_func available
|
|
return True
|
|
|
|
if current_platform.is_rocm():
|
|
# Use the flag set during module import to check if
|
|
# upstream flash-attn was successfully imported
|
|
return _ROCM_FLASH_ATTN_AVAILABLE
|
|
|
|
return False
|