[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,
)
# 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__)
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):
accept_output_buffer: bool = True
@@ -56,14 +74,6 @@ class FlashAttentionImpl(AttentionImpl):
attn_metadata: AttentionMetadata = None,
) -> torch.Tensor:
"""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)
attention_mask = attn_metadata.attn_mask if attn_metadata is not None else None
# 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)
else:
out: torch.Tensor = flash_attn_func(
out = flash_attn_func(
query,
key,
value,
causal=self.causal,
softmax_scale=self.softmax_scale,
)
# FA3 may return (out, lse) tuple, FA2 returns just out
if isinstance(out, tuple):
out = out[0]
return out
def forward_npu(
@@ -3,30 +3,53 @@
# Copyright (c) 2024, Jiarui Fang.
# Adapted from https://github.com/feifeibear/long-context-attention
# test if flash_attn is available
# test if flash_attn (FA2) is available
try:
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
except ImportError:
except (ImportError, ModuleNotFoundError):
HAS_FLASH_ATTN = False
try:
from flash_attn_interface import _flash_attn_backward as flash_attn_func_hopper_backward # noqa: F401
from flash_attn_interface import _flash_attn_forward as flash_attn_forward_hopper # noqa: F401
from flash_attn_interface import flash_attn_func as flash3_attn_func # noqa: F401
# FA3 detection: try multiple sources (forward only, no backward needed for inference)
# Source 1: flash_attn_interface (from flash-attention source build)
# Source 2: fa3_fwd_interface (from fa3-fwd PyPI package, supports Ampere/Ada/Hopper)
# 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
except ImportError:
HAS_FLASH_ATTN_HOPPER = False
# Try flash_attn_interface first (from flash-attention source build)
try:
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:
from flashinfer.prefill import single_prefill_with_kv_cache # noqa: F401
HAS_FLASHINFER = True
except ImportError:
except (ImportError, ModuleNotFoundError):
HAS_FLASHINFER = False
try:
@@ -34,26 +57,26 @@ try:
from aiter import flash_attn_func as flash_attn_func_aiter # noqa: F401
HAS_AITER = True
except ImportError:
except (ImportError, ModuleNotFoundError):
HAS_AITER = False
try:
import sageattention # noqa: F401
HAS_SAGE_ATTENTION = True
except ImportError:
except (ImportError, ModuleNotFoundError):
HAS_SAGE_ATTENTION = False
try:
import spas_sage_attn # noqa: F401
HAS_SPARSE_SAGE_ATTENTION = True
except ImportError:
except (ImportError, ModuleNotFoundError):
HAS_SPARSE_SAGE_ATTENTION = False
try:
import torch_npu # noqa: F401
HAS_NPU = True
except ImportError:
except (ImportError, ModuleNotFoundError):
HAS_NPU = False
@@ -6,7 +6,13 @@ import math
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_efficient_attention = torch.ops.aten._scaled_dot_product_efficient_attention
@@ -26,23 +32,11 @@ if HAS_FLASH_ATTN:
import flash_attn
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:
from flashinfer.prefill import single_prefill_with_kv_cache
_LOG2_E = math.log2(math.e)
if HAS_NPU:
import torch_npu
def pytorch_attn_forward(
q: torch.Tensor,
@@ -146,52 +140,35 @@ def flash_attn_forward(
return block_out, block_lse
def flash_attn3_func_forward(
q, k, v, dropout_p, softmax_scale, causal, window_size, softcap, alibi_slopes, return_softmax
):
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)
def fa3_forward(q, k, v, dropout_p, softmax_scale, causal, window_size, softcap, alibi_slopes, return_softmax):
"""FA3 forward pass for inference.
out, softmax_lse, *unused = flash_attn_forward_hopper(
q=q,
k=k,
v=v,
k_new=None,
v_new=None,
qv=None,
out=None,
cu_seqlens_q=None,
cu_seqlens_k=None,
cu_seqlens_k_new=None,
seqused_q=None,
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,
FA3 supports Ampere, Ada, and Hopper GPUs. Dropout is ignored since FA3 is inference-only.
Uses low-level API (_flash_attn_forward) which always returns softmax_lse,
required for Ring Attention's correct accumulation.
"""
assert HAS_FA3, "FA3 is not available"
assert fa3_fwd_func is not None, "FA3 low-level API (fa3_fwd_func) not available"
# Low-level API always returns (out, softmax_lse, S_dmask, rng_state)
out, softmax_lse, *_ = fa3_fwd_func(
q,
k,
v,
softmax_scale=softmax_scale,
causal=False,
window_size=(-1, -1),
attention_chunk=0,
softcap=0.0,
rotary_interleaved=True,
scheduler_metadata=None,
num_splits=0,
pack_gqa=None,
sm_margin=0,
causal=causal,
window_size_left=window_size[0] if window_size else -1,
window_size_right=window_size[1] if window_size else -1,
softcap=softcap if softcap else 0.0,
)
return out, softmax_lse
# Legacy alias for backward compatibility
flash_attn3_func_forward = fa3_forward
def flash_attn_forward_aiter(
q,
k,
@@ -264,20 +241,3 @@ def flashinfer_attn_forward(
raise ValueError(f"Invalid input shape: {q.shape}")
lse = lse / _LOG2_E
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
from .ring_globals import (
HAS_NPU,
HAS_SAGE_ATTENTION,
HAS_SPARSE_SAGE_ATTENTION,
)
@@ -28,9 +27,6 @@ if HAS_SAGE_ATTENTION:
if HAS_SPARSE_SAGE_ATTENTION:
from spas_sage_attn.autotune import SparseAttentionMeansim
if HAS_NPU:
from torch_npu import npu_fused_infer_attention_score
class AttnType(Enum):
AITER = "aiter"
@@ -44,7 +40,6 @@ class AttnType(Enum):
SAGE_FP8 = "sage_fp8"
SAGE_FP8_SM90 = "sage_fp8_sm90"
SPARSE_SAGE = "sparse_sage"
NPU = "npu"
@classmethod
def from_string(cls, s: str):
@@ -157,11 +152,6 @@ def select_flash_attn_impl(
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:
return attn_processor
@@ -17,17 +17,53 @@ import torch.nn.functional as F
from vllm_omni.platforms import current_omni_platform
if current_omni_platform.is_rocm():
from vllm._aiter_ops import is_aiter_found_and_supported
# Flash Attention function detection with fallback chain
flash_attn_func = None
flash_attn_varlen_func = None
# Choose to enable this by default on ROCm
# Whenever possible as it is the fastest backend
if is_aiter_found_and_supported():
from aiter import flash_attn_func, flash_attn_varlen_func # noqa: F401
else:
raise ImportError("Aiter is not found and supported on currentROCm device.")
if current_omni_platform.is_rocm():
# ROCm: try Aiter first
try:
from vllm._aiter_ops import is_aiter_found_and_supported
if is_aiter_found_and_supported():
from aiter import flash_attn_func, flash_attn_varlen_func # noqa: F401
except (ImportError, ModuleNotFoundError):
pass
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):
+12 -7
View File
@@ -10,7 +10,7 @@ import torch
from vllm.logger import init_logger
# 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.parallel.base import (
ParallelAttentionContext,
@@ -116,11 +116,14 @@ class RingParallelAttention:
except Exception:
backend_pref = None
# Fallback for FP32 or if Flash Attention is not available
if query.dtype == torch.float32 or not HAS_FLASH_ATTN:
if not HAS_FLASH_ATTN and backend_pref != "sdpa":
# Determine attention type with fallback chain: FA3 -> FA2 -> SDPA
# FP32 is not supported by Flash Attention, force 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.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"
# Extract joint tensors
@@ -150,6 +153,9 @@ class RingParallelAttention:
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(
query,
key,
@@ -161,9 +167,8 @@ class RingParallelAttention:
softcap=0.0,
alibi_slopes=None,
deterministic=False,
# return_attn_probs=False, # Removed as it might not be supported in signature
group=self._sp_group.ring_group,
attn_type=AttnType.FA,
attn_type=attn_type,
joint_tensor_key=joint_key,
joint_tensor_value=joint_value,
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:
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__
if __version__ < "2.6.0":
raise ImportError("install flash_attn >= 2.6.0")
return True
except ImportError:
except (ImportError, ModuleNotFoundError):
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
def get_packages_info(self) -> dict:
+18 -6
View File
@@ -39,22 +39,34 @@ class CudaOmniPlatform(OmniPlatform, CudaPlatformBase):
selected_backend: str | None,
head_size: int,
) -> str:
from vllm_omni.diffusion.envs import PACKAGES_CHECKER
# Check compute capability for Flash Attention support
# Flash Attention requires compute capability >= 8.0 and < 10.0
compute_capability = cls.get_device_capability()
flash_attn_supported = False
compute_supported = False
if compute_capability is not None:
major, minor = compute_capability
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:
backend_upper = selected_backend.upper()
if backend_upper == "FLASH_ATTN" and not flash_attn_supported:
logger.warning(
"Flash Attention requires GPU with compute capability >= 8.0 "
"and < 10.0. Falling back to TORCH_SDPA backend."
)
if not compute_supported:
logger.warning(
"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")
return DiffusionAttentionBackendEnum.TORCH_SDPA.get_path()
backend = DiffusionAttentionBackendEnum[backend_upper]