[Diffusion][Bugfix] Fix the flash_attn backends selection logic (#983)

Signed-off-by: mxuax <mxuax@connect.ust.hk>
Signed-off-by: XU Mingshi <91017482+mxuax@users.noreply.github.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
This commit is contained in:
XU MingshiandHongsheng Liu authored and GitHub committed 2026-01-29 16:44:58 +08:00
1 parent def39561ae
commit 5b8ade8446
8 files changed
+185 -128

No files matched your search

@@ -10,8 +10,26 @@ from vllm_omni.diffusion.attention.backends.abstract import (
AttentionMetadata, AttentionMetadata,
) )
# Import flash attention functions with fallback chain from utils/fa.py
# FA3 (fa3_fwd_interface) -> FA3 (flash_attn_interface) -> FA2 (flash_attn)
from vllm_omni.diffusion.attention.backends.utils.fa import (
HAS_FLASH_ATTN,
_pad_input,
_unpad_input,
_upad_input,
flash_attn_func,
flash_attn_varlen_func,
)
logger = init_logger(__name__) logger = init_logger(__name__)
if not HAS_FLASH_ATTN:
raise ImportError(
"FlashAttentionBackend requires Flash Attention. "
"Please install one of: fa3-fwd, flash-attention, or flash-attn. "
"Otherwise, use SDPA backend by setting DIFFUSION_ATTENTION_BACKEND=TORCH_SDPA"
)
class FlashAttentionBackend(AttentionBackend): class FlashAttentionBackend(AttentionBackend):
accept_output_buffer: bool = True accept_output_buffer: bool = True
@@ -56,14 +74,6 @@ class FlashAttentionImpl(AttentionImpl):
attn_metadata: AttentionMetadata = None, attn_metadata: AttentionMetadata = None,
) -> torch.Tensor: ) -> torch.Tensor:
"""CUDA/ROCm flash attention implementation.""" """CUDA/ROCm flash attention implementation."""
from vllm_omni.diffusion.attention.backends.utils.fa import (
_pad_input,
_unpad_input,
_upad_input,
flash_attn_func,
flash_attn_varlen_func,
)
query_length = query.size(1) query_length = query.size(1)
attention_mask = attn_metadata.attn_mask if attn_metadata is not None else None attention_mask = attn_metadata.attn_mask if attn_metadata is not None else None
# Contains at least one padding token in the sequence # Contains at least one padding token in the sequence
@@ -92,13 +102,16 @@ class FlashAttentionImpl(AttentionImpl):
out = _pad_input(out_unpad, indices_q, query.size(0), query_length) out = _pad_input(out_unpad, indices_q, query.size(0), query_length)
else: else:
out: torch.Tensor = flash_attn_func( out = flash_attn_func(
query, query,
key, key,
value, value,
causal=self.causal, causal=self.causal,
softmax_scale=self.softmax_scale, softmax_scale=self.softmax_scale,
) )
# FA3 may return (out, lse) tuple, FA2 returns just out
if isinstance(out, tuple):
out = out[0]
return out return out
def forward_npu( def forward_npu(
@@ -3,30 +3,53 @@
# Copyright (c) 2024, Jiarui Fang. # Copyright (c) 2024, Jiarui Fang.
# Adapted from https://github.com/feifeibear/long-context-attention # Adapted from https://github.com/feifeibear/long-context-attention
# test if flash_attn (FA2) is available
# test if flash_attn is available
try: try:
import flash_attn # noqa: F401 import flash_attn # noqa: F401
from flash_attn.flash_attn_interface import _flash_attn_backward, _flash_attn_forward # noqa: F401 from flash_attn.flash_attn_interface import _flash_attn_forward # noqa: F401
HAS_FLASH_ATTN = True HAS_FLASH_ATTN = True
except ImportError: except (ImportError, ModuleNotFoundError):
HAS_FLASH_ATTN = False HAS_FLASH_ATTN = False
try: # FA3 detection: try multiple sources (forward only, no backward needed for inference)
from flash_attn_interface import _flash_attn_backward as flash_attn_func_hopper_backward # noqa: F401 # Source 1: flash_attn_interface (from flash-attention source build)
from flash_attn_interface import _flash_attn_forward as flash_attn_forward_hopper # noqa: F401 # Source 2: fa3_fwd_interface (from fa3-fwd PyPI package, supports Ampere/Ada/Hopper)
from flash_attn_interface import flash_attn_func as flash3_attn_func # noqa: F401 # Note: FA3 high-level API may or may not return softmax_lse depending on version.
# For Ring Attention which requires LSE, we fall back to low-level API if needed.
HAS_FA3 = False
fa3_fwd_func = None # Low-level forward function (_flash_attn_forward)
fa3_attn_func = None # High-level attention function (flash_attn_func)
HAS_FLASH_ATTN_HOPPER = True # Try flash_attn_interface first (from flash-attention source build)
except ImportError: try:
HAS_FLASH_ATTN_HOPPER = False from flash_attn_interface import _flash_attn_forward as fa3_fwd_func # noqa: F401
from flash_attn_interface import flash_attn_func as fa3_attn_func # noqa: F401
HAS_FA3 = True
except (ImportError, ModuleNotFoundError):
pass
# Fallback: try fa3_fwd_interface (PyPI package, supports Ampere/Ada/Hopper)
if not HAS_FA3:
try:
from fa3_fwd_interface import _flash_attn_forward as fa3_fwd_func # noqa: F401
from fa3_fwd_interface import flash_attn_func as fa3_attn_func # noqa: F401
HAS_FA3 = True
except (ImportError, ModuleNotFoundError):
pass
# Legacy aliases for backward compatibility
HAS_FLASH_ATTN_HOPPER = HAS_FA3
flash_attn_forward_hopper = fa3_fwd_func
flash3_attn_func = fa3_attn_func
try: try:
from flashinfer.prefill import single_prefill_with_kv_cache # noqa: F401 from flashinfer.prefill import single_prefill_with_kv_cache # noqa: F401
HAS_FLASHINFER = True HAS_FLASHINFER = True
except ImportError: except (ImportError, ModuleNotFoundError):
HAS_FLASHINFER = False HAS_FLASHINFER = False
try: try:
@@ -34,26 +57,26 @@ try:
from aiter import flash_attn_func as flash_attn_func_aiter # noqa: F401 from aiter import flash_attn_func as flash_attn_func_aiter # noqa: F401
HAS_AITER = True HAS_AITER = True
except ImportError: except (ImportError, ModuleNotFoundError):
HAS_AITER = False HAS_AITER = False
try: try:
import sageattention # noqa: F401 import sageattention # noqa: F401
HAS_SAGE_ATTENTION = True HAS_SAGE_ATTENTION = True
except ImportError: except (ImportError, ModuleNotFoundError):
HAS_SAGE_ATTENTION = False HAS_SAGE_ATTENTION = False
try: try:
import spas_sage_attn # noqa: F401 import spas_sage_attn # noqa: F401
HAS_SPARSE_SAGE_ATTENTION = True HAS_SPARSE_SAGE_ATTENTION = True
except ImportError: except (ImportError, ModuleNotFoundError):
HAS_SPARSE_SAGE_ATTENTION = False HAS_SPARSE_SAGE_ATTENTION = False
try: try:
import torch_npu # noqa: F401 import torch_npu # noqa: F401
HAS_NPU = True HAS_NPU = True
except ImportError: except (ImportError, ModuleNotFoundError):
HAS_NPU = False HAS_NPU = False
@@ -6,7 +6,13 @@ import math
import torch import torch
from .ring_globals import HAS_AITER, HAS_FLASH_ATTN, HAS_FLASH_ATTN_HOPPER, HAS_FLASHINFER, HAS_NPU from .ring_globals import (
HAS_AITER,
HAS_FA3,
HAS_FLASH_ATTN,
HAS_FLASHINFER,
fa3_fwd_func,
)
_scaled_dot_product_flash_attention = torch.ops.aten._scaled_dot_product_flash_attention _scaled_dot_product_flash_attention = torch.ops.aten._scaled_dot_product_flash_attention
_scaled_dot_product_efficient_attention = torch.ops.aten._scaled_dot_product_efficient_attention _scaled_dot_product_efficient_attention = torch.ops.aten._scaled_dot_product_efficient_attention
@@ -26,23 +32,11 @@ if HAS_FLASH_ATTN:
import flash_attn import flash_attn
from flash_attn.flash_attn_interface import _flash_attn_forward from flash_attn.flash_attn_interface import _flash_attn_forward
if HAS_FLASH_ATTN_HOPPER:
from flash_attn_interface import _flash_attn_backward as flash_attn_func_hopper_backward
from flash_attn_interface import _flash_attn_forward as flash_attn_forward_hopper
from flash_attn_interface import flash_attn_func as flash3_attn_func
else:
flash_attn_forward_hopper = None
flash_attn_func_hopper_backward = None
flash3_attn_func = None
if HAS_FLASHINFER: if HAS_FLASHINFER:
from flashinfer.prefill import single_prefill_with_kv_cache from flashinfer.prefill import single_prefill_with_kv_cache
_LOG2_E = math.log2(math.e) _LOG2_E = math.log2(math.e)
if HAS_NPU:
import torch_npu
def pytorch_attn_forward( def pytorch_attn_forward(
q: torch.Tensor, q: torch.Tensor,
@@ -146,52 +140,35 @@ def flash_attn_forward(
return block_out, block_lse return block_out, block_lse
def flash_attn3_func_forward( def fa3_forward(q, k, v, dropout_p, softmax_scale, causal, window_size, softcap, alibi_slopes, return_softmax):
q, k, v, dropout_p, softmax_scale, causal, window_size, softcap, alibi_slopes, return_softmax """FA3 forward pass for inference.
):
assert HAS_FLASH_ATTN_HOPPER
# current signature of flash_attn_forward_hopper:
# (q, k, v, softmax_scale, causal, window_size, descale_q=None, descale_k=None, descale_v=None, gqa_parallel=False)
out, softmax_lse, *unused = flash_attn_forward_hopper( FA3 supports Ampere, Ada, and Hopper GPUs. Dropout is ignored since FA3 is inference-only.
q=q, Uses low-level API (_flash_attn_forward) which always returns softmax_lse,
k=k, required for Ring Attention's correct accumulation.
v=v, """
k_new=None, assert HAS_FA3, "FA3 is not available"
v_new=None, assert fa3_fwd_func is not None, "FA3 low-level API (fa3_fwd_func) not available"
qv=None,
out=None, # Low-level API always returns (out, softmax_lse, S_dmask, rng_state)
cu_seqlens_q=None, out, softmax_lse, *_ = fa3_fwd_func(
cu_seqlens_k=None, q,
cu_seqlens_k_new=None, k,
seqused_q=None, v,
seqused_k=None,
max_seqlen_q=None,
max_seqlen_k=None,
page_table=None,
kv_batch_idx=None,
leftpad_k=None,
rotary_cos=None,
rotary_sin=None,
seqlens_rotary=None,
q_descale=None,
k_descale=None,
v_descale=None,
softmax_scale=softmax_scale, softmax_scale=softmax_scale,
causal=False, causal=causal,
window_size=(-1, -1), window_size_left=window_size[0] if window_size else -1,
attention_chunk=0, window_size_right=window_size[1] if window_size else -1,
softcap=0.0, softcap=softcap if softcap else 0.0,
rotary_interleaved=True,
scheduler_metadata=None,
num_splits=0,
pack_gqa=None,
sm_margin=0,
) )
return out, softmax_lse return out, softmax_lse
# Legacy alias for backward compatibility
flash_attn3_func_forward = fa3_forward
def flash_attn_forward_aiter( def flash_attn_forward_aiter(
q, q,
k, k,
@@ -264,20 +241,3 @@ def flashinfer_attn_forward(
raise ValueError(f"Invalid input shape: {q.shape}") raise ValueError(f"Invalid input shape: {q.shape}")
lse = lse / _LOG2_E lse = lse / _LOG2_E
return out, lse return out, lse
def npu_attn_forward(q, k, v, softmax_scale=None, layout="BSND"):
assert HAS_NPU, "torch_npu is not available"
softmax_scale = q.shape[-1] ** (-0.5)
block_out, block_lse = torch_npu.npu_fused_infer_attention_score(
q,
k,
v,
num_heads=q.shape[-2],
input_layout=layout,
scale=softmax_scale,
softmax_lse_flag=True,
pre_tokens=65535,
next_tokens=65535,
)
return block_out, block_lse.squeeze(dim=-1)
@@ -10,7 +10,6 @@ from functools import partial
import torch import torch
from .ring_globals import ( from .ring_globals import (
HAS_NPU,
HAS_SAGE_ATTENTION, HAS_SAGE_ATTENTION,
HAS_SPARSE_SAGE_ATTENTION, HAS_SPARSE_SAGE_ATTENTION,
) )
@@ -28,9 +27,6 @@ if HAS_SAGE_ATTENTION:
if HAS_SPARSE_SAGE_ATTENTION: if HAS_SPARSE_SAGE_ATTENTION:
from spas_sage_attn.autotune import SparseAttentionMeansim from spas_sage_attn.autotune import SparseAttentionMeansim
if HAS_NPU:
from torch_npu import npu_fused_infer_attention_score
class AttnType(Enum): class AttnType(Enum):
AITER = "aiter" AITER = "aiter"
@@ -44,7 +40,6 @@ class AttnType(Enum):
SAGE_FP8 = "sage_fp8" SAGE_FP8 = "sage_fp8"
SAGE_FP8_SM90 = "sage_fp8_sm90" SAGE_FP8_SM90 = "sage_fp8_sm90"
SPARSE_SAGE = "sparse_sage" SPARSE_SAGE = "sparse_sage"
NPU = "npu"
@classmethod @classmethod
def from_string(cls, s: str): def from_string(cls, s: str):
@@ -157,11 +152,6 @@ def select_flash_attn_impl(
return fn return fn
elif impl_type == AttnType.NPU:
if not HAS_NPU:
raise ImportError("torch_npu is not available!")
return npu_fused_infer_attention_score
elif attn_processor is not None: elif attn_processor is not None:
return attn_processor return attn_processor
@@ -17,17 +17,53 @@ import torch.nn.functional as F
from vllm_omni.platforms import current_omni_platform from vllm_omni.platforms import current_omni_platform
if current_omni_platform.is_rocm(): # Flash Attention function detection with fallback chain
from vllm._aiter_ops import is_aiter_found_and_supported flash_attn_func = None
flash_attn_varlen_func = None
# Choose to enable this by default on ROCm if current_omni_platform.is_rocm():
# Whenever possible as it is the fastest backend # ROCm: try Aiter first
if is_aiter_found_and_supported(): try:
from aiter import flash_attn_func, flash_attn_varlen_func # noqa: F401 from vllm._aiter_ops import is_aiter_found_and_supported
else:
raise ImportError("Aiter is not found and supported on currentROCm device.") if is_aiter_found_and_supported():
from aiter import flash_attn_func, flash_attn_varlen_func # noqa: F401
except (ImportError, ModuleNotFoundError):
pass
else: else:
from fa3_fwd_interface import flash_attn_func, flash_attn_varlen_func # noqa: F401 # CUDA: try FA3 -> FA2 fallback chain
# Try FA3 from fa3-fwd PyPI package
try:
from fa3_fwd_interface import flash_attn_func, flash_attn_varlen_func # noqa: F401
except (ImportError, ModuleNotFoundError):
pass
# Fallback: Try FA3 from flash-attention source build
if flash_attn_func is None:
try:
from flash_attn_interface import flash_attn_func, flash_attn_varlen_func # noqa: F401
except (ImportError, ModuleNotFoundError):
pass
# Fallback: Try FA2 from flash-attn package (try multiple import paths)
if flash_attn_func is None:
try:
from flash_attn import flash_attn_func, flash_attn_varlen_func # noqa: F401
except (ImportError, ModuleNotFoundError):
pass
if flash_attn_func is None:
try:
from flash_attn.flash_attn_interface import ( # noqa: F401
flash_attn_func,
flash_attn_varlen_func,
)
except (ImportError, ModuleNotFoundError):
pass
# If no FA backend available, SDPA backend will be selected at the platform level
# flash_attn_func and flash_attn_varlen_func will be None
HAS_FLASH_ATTN = flash_attn_func is not None
def _index_first_axis(tensor, indices): def _index_first_axis(tensor, indices):
+12 -7
View File
@@ -10,7 +10,7 @@ import torch
from vllm.logger import init_logger from vllm.logger import init_logger
# import torch.distributed as dist # Not used directly here, but good practice if needed # import torch.distributed as dist # Not used directly here, but good practice if needed
from vllm_omni.diffusion.attention.backends.ring.ring_globals import HAS_FLASH_ATTN from vllm_omni.diffusion.attention.backends.ring.ring_globals import HAS_FA3, HAS_FLASH_ATTN
from vllm_omni.diffusion.attention.backends.ring.ring_selector import AttnType from vllm_omni.diffusion.attention.backends.ring.ring_selector import AttnType
from vllm_omni.diffusion.attention.parallel.base import ( from vllm_omni.diffusion.attention.parallel.base import (
ParallelAttentionContext, ParallelAttentionContext,
@@ -116,11 +116,14 @@ class RingParallelAttention:
except Exception: except Exception:
backend_pref = None backend_pref = None
# Fallback for FP32 or if Flash Attention is not available # Determine attention type with fallback chain: FA3 -> FA2 -> SDPA
if query.dtype == torch.float32 or not HAS_FLASH_ATTN: # FP32 is not supported by Flash Attention, force SDPA
if not HAS_FLASH_ATTN and backend_pref != "sdpa": if query.dtype == torch.float32:
backend_pref = "sdpa"
elif not HAS_FA3 and not HAS_FLASH_ATTN:
if backend_pref != "sdpa":
logger = init_logger(__name__) logger = init_logger(__name__)
logger.warning_once("Flash Attention is not available! Force enabling SDPA.") logger.warning_once("Flash Attention (FA2/FA3) is not available! Force enabling SDPA.")
backend_pref = "sdpa" backend_pref = "sdpa"
# Extract joint tensors # Extract joint tensors
@@ -150,6 +153,9 @@ class RingParallelAttention:
from vllm_omni.diffusion.attention.backends.ring_flash_attn import ring_flash_attn_func from vllm_omni.diffusion.attention.backends.ring_flash_attn import ring_flash_attn_func
# Prefer FA3 over FA2 for better performance (FA3 supports Ampere/Ada/Hopper)
attn_type = AttnType.FA3 if HAS_FA3 else AttnType.FA
return ring_flash_attn_func( return ring_flash_attn_func(
query, query,
key, key,
@@ -161,9 +167,8 @@ class RingParallelAttention:
softcap=0.0, softcap=0.0,
alibi_slopes=None, alibi_slopes=None,
deterministic=False, deterministic=False,
# return_attn_probs=False, # Removed as it might not be supported in signature
group=self._sp_group.ring_group, group=self._sp_group.ring_group,
attn_type=AttnType.FA, attn_type=attn_type,
joint_tensor_key=joint_key, joint_tensor_key=joint_key,
joint_tensor_value=joint_value, joint_tensor_value=joint_value,
joint_strategy=joint_strategy, joint_strategy=joint_strategy,
+20 -2
View File
@@ -66,14 +66,32 @@ class PackagesEnvChecker:
if "Turing" in gpu_name or "Tesla" in gpu_name or "T4" in gpu_name: if "Turing" in gpu_name or "Tesla" in gpu_name or "T4" in gpu_name:
return False return False
# Check for any FA backend: FA3 (fa3_fwd_interface, flash_attn_interface) or FA2 (flash_attn)
# Try FA3 from fa3-fwd PyPI package
try:
import fa3_fwd_interface # noqa: F401
return True
except (ImportError, ModuleNotFoundError):
pass
# Try FA3 from flash-attention source build
try:
import flash_attn_interface # noqa: F401
return True
except (ImportError, ModuleNotFoundError):
pass
# Try FA2 from flash-attn package
from flash_attn import __version__ from flash_attn import __version__
if __version__ < "2.6.0": if __version__ < "2.6.0":
raise ImportError("install flash_attn >= 2.6.0") raise ImportError("install flash_attn >= 2.6.0")
return True return True
except ImportError: except (ImportError, ModuleNotFoundError):
if not packages_info.get("has_aiter", False): if not packages_info.get("has_aiter", False):
logger.warning('Flash Attention library "flash_attn" not found, using pytorch attention implementation') logger.warning("No Flash Attention backend found, using pytorch SDPA implementation")
return False return False
def get_packages_info(self) -> dict: def get_packages_info(self) -> dict:
+18 -6
View File
@@ -39,22 +39,34 @@ class CudaOmniPlatform(OmniPlatform, CudaPlatformBase):
selected_backend: str | None, selected_backend: str | None,
head_size: int, head_size: int,
) -> str: ) -> str:
from vllm_omni.diffusion.envs import PACKAGES_CHECKER
# Check compute capability for Flash Attention support # Check compute capability for Flash Attention support
# Flash Attention requires compute capability >= 8.0 and < 10.0 # Flash Attention requires compute capability >= 8.0 and < 10.0
compute_capability = cls.get_device_capability() compute_capability = cls.get_device_capability()
flash_attn_supported = False compute_supported = False
if compute_capability is not None: if compute_capability is not None:
major, minor = compute_capability major, minor = compute_capability
capability = major * 10 + minor capability = major * 10 + minor
flash_attn_supported = 80 <= capability < 100 compute_supported = 80 <= capability < 100
# Check if FA packages are available
packages_info = PACKAGES_CHECKER.get_packages_info()
packages_available = packages_info.get("has_flash_attn", False)
# Both compute capability and packages must be available for FA
flash_attn_supported = compute_supported and packages_available
if selected_backend is not None: if selected_backend is not None:
backend_upper = selected_backend.upper() backend_upper = selected_backend.upper()
if backend_upper == "FLASH_ATTN" and not flash_attn_supported: if backend_upper == "FLASH_ATTN" and not flash_attn_supported:
logger.warning( if not compute_supported:
"Flash Attention requires GPU with compute capability >= 8.0 " logger.warning(
"and < 10.0. Falling back to TORCH_SDPA backend." "Flash Attention requires GPU with compute capability >= 8.0 "
) "and < 10.0. Falling back to TORCH_SDPA backend."
)
elif not packages_available:
logger.warning("Flash Attention packages not available. Falling back to TORCH_SDPA backend.")
logger.info("Defaulting to diffusion attention backend SDPA") logger.info("Defaulting to diffusion attention backend SDPA")
return DiffusionAttentionBackendEnum.TORCH_SDPA.get_path() return DiffusionAttentionBackendEnum.TORCH_SDPA.get_path()
backend = DiffusionAttentionBackendEnum[backend_upper] backend = DiffusionAttentionBackendEnum[backend_upper]