Files
vllm-omni/tests/diffusion/attention/test_cuda_backend_selection.py
95a9311696 [CI] Select mirror_hardwares presets via MIRROR_HW (H100/B200) (#5543)
Signed-off-by: wangyu <410167048@qq.com>
Signed-off-by: lishunyang12 <lishunyang12@163.com>
Signed-off-by: [Your Name] <your.email@example.com>
Signed-off-by: wangyu <53896905+yenuo26@users.noreply.github.com>
Signed-off-by: Bo Li <22713281+bobboli@users.noreply.github.com>
Co-authored-by: lishunyang12 <lishunyang12@163.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Bo Li <22713281+bobboli@users.noreply.github.com>
2026-09-02 15:39:49 +00:00

266 lines
11 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
import sys
import types
from types import SimpleNamespace
import pytest
import torch
from vllm.platforms import current_platform
from vllm.platforms.interface import DeviceCapability
from vllm_omni.diffusion.attention.backends.registry import DiffusionAttentionBackendEnum
from vllm_omni.diffusion.attention.layer import Attention
from vllm_omni.diffusion.attention.selector import _cached_get_backend_cls
from vllm_omni.diffusion.config import set_current_diffusion_config
from vllm_omni.diffusion.data import AttentionConfig
from vllm_omni.diffusion.diffusion_kv.config import DiffusionKVCacheMode
from vllm_omni.diffusion.envs import PACKAGES_CHECKER
# Importing CudaOmniPlatform pulls in vllm.platforms.cuda, which imports the
# CUDA-only ``vllm._C_stable_libtorch`` extension at module top level. That
# extension is absent on non-CUDA builds (e.g. Intel/XPU), so skip the whole
# module there before the import can crash collection. ``is_cuda()`` resolves
# the platform without importing cuda.py.
if not current_platform.is_cuda():
pytest.skip("CUDA-only diffusion backend selection tests", allow_module_level=True)
from vllm_omni.platforms.cuda.platform import CudaOmniPlatform # noqa: E402
pytestmark = [pytest.mark.core_model, pytest.mark.diffusion, pytest.mark.cpu]
def _blackwell_sm120(monkeypatch: pytest.MonkeyPatch, *, cudnn_version: int = 90500) -> None:
monkeypatch.setattr(
CudaOmniPlatform,
"get_device_capability",
classmethod(lambda cls, device_id=0: DeviceCapability(12, 0)),
)
monkeypatch.setattr(PACKAGES_CHECKER, "get_packages_info", lambda: {"has_flash_attn": False})
monkeypatch.setattr(CudaOmniPlatform, "has_flash_attn_4", classmethod(lambda cls: False))
monkeypatch.setattr(torch.backends.cudnn, "version", lambda: cudnn_version)
def _install_dummy_flashinfer(monkeypatch: pytest.MonkeyPatch) -> None:
"""Top-level ``flashinfer`` only — no ``prefill`` wrapper (partial install)."""
monkeypatch.setitem(sys.modules, "flashinfer", types.ModuleType("flashinfer"))
# A previously imported real submodule must not satisfy the wrapper probe.
monkeypatch.delitem(sys.modules, "flashinfer.prefill", raising=False)
def _install_dummy_flashinfer_prefill(monkeypatch: pytest.MonkeyPatch) -> None:
"""FlashInfer stub that exposes the prefill wrapper FLASHINFER_ATTN needs."""
prefill = types.ModuleType("flashinfer.prefill")
setattr(prefill, "BatchPrefillWithRaggedKVCacheWrapper", type("BatchPrefillWithRaggedKVCacheWrapper", (), {}))
pkg = types.ModuleType("flashinfer")
setattr(pkg, "prefill", prefill)
monkeypatch.setitem(sys.modules, "flashinfer", pkg)
monkeypatch.setitem(sys.modules, "flashinfer.prefill", prefill)
def test_auto_selects_cudnn_for_supported_blackwell_head_size(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls(None, head_size=128)
assert path == DiffusionAttentionBackendEnum.CUDNN_ATTN.get_path()
def test_auto_routes_incompatible_head_size_to_sdpa(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
_install_dummy_flashinfer(monkeypatch)
# 320 is outside cuDNN FMHA (<=256, multiple of 8) and FlashInfer {64,128,256}.
path = CudaOmniPlatform.get_diffusion_attn_backend_cls(None, head_size=320)
assert path == DiffusionAttentionBackendEnum.TORCH_SDPA.get_path()
def test_auto_routes_non_multiple_head_size_to_sdpa_without_flashinfer(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
monkeypatch.setitem(sys.modules, "flashinfer", None)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls(None, head_size=12)
assert path == DiffusionAttentionBackendEnum.TORCH_SDPA.get_path()
def test_auto_selects_flashinfer_when_cudnn_too_old(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch, cudnn_version=90499)
_install_dummy_flashinfer_prefill(monkeypatch)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls(None, head_size=128)
assert path == DiffusionAttentionBackendEnum.FLASHINFER_ATTN.get_path()
def test_auto_skips_flashinfer_when_only_toplevel_package(monkeypatch: pytest.MonkeyPatch):
"""A stub that only imports ``flashinfer`` must not be auto-selected."""
_blackwell_sm120(monkeypatch, cudnn_version=90499)
_install_dummy_flashinfer(monkeypatch)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls(None, head_size=128)
assert path == DiffusionAttentionBackendEnum.TORCH_SDPA.get_path()
def test_explicit_flashinfer_raises_without_prefill_wrapper(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
_install_dummy_flashinfer(monkeypatch)
with pytest.raises(ValueError, match="BatchPrefillWithRaggedKVCacheWrapper"):
CudaOmniPlatform.get_diffusion_attn_backend_cls("FLASHINFER_ATTN", head_size=128)
def test_auto_skips_flashinfer_for_unsupported_head_size_when_cudnn_too_old(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch, cudnn_version=90499)
_install_dummy_flashinfer(monkeypatch)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls(None, head_size=72)
assert path == DiffusionAttentionBackendEnum.TORCH_SDPA.get_path()
def test_explicit_cudnn_raises_for_incompatible_head_size(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
with pytest.raises(ValueError, match="head_size=12 is unsupported"):
CudaOmniPlatform.get_diffusion_attn_backend_cls("CUDNN_ATTN", head_size=12)
def test_explicit_cudnn_accepts_supported_head_size(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls("CUDNN_ATTN", head_size=72)
assert path == DiffusionAttentionBackendEnum.CUDNN_ATTN.get_path()
def test_explicit_cudnn_skips_head_size_check_for_capability_probe(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls("CUDNN_ATTN", head_size=-1)
assert path == DiffusionAttentionBackendEnum.CUDNN_ATTN.get_path()
@pytest.mark.parametrize("head_size", [32, 320])
def test_explicit_flashinfer_raises_for_unsupported_head_size(monkeypatch: pytest.MonkeyPatch, head_size: int):
_blackwell_sm120(monkeypatch)
_install_dummy_flashinfer_prefill(monkeypatch)
with pytest.raises(ValueError, match=f"head_size={head_size} is unsupported"):
CudaOmniPlatform.get_diffusion_attn_backend_cls("FLASHINFER_ATTN", head_size=head_size)
def test_explicit_flashinfer_accepts_supported_head_size(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
_install_dummy_flashinfer_prefill(monkeypatch)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls("FLASHINFER_ATTN", head_size=128)
assert path == DiffusionAttentionBackendEnum.FLASHINFER_ATTN.get_path()
def test_explicit_flashinfer_skips_head_size_check_for_capability_probe(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
_install_dummy_flashinfer_prefill(monkeypatch)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls("FLASHINFER_ATTN", head_size=-1)
assert path == DiffusionAttentionBackendEnum.FLASHINFER_ATTN.get_path()
def test_auto_selects_cudnn_for_unknown_head_size_probe(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
_install_dummy_flashinfer_prefill(monkeypatch)
path = CudaOmniPlatform.get_diffusion_attn_backend_cls(None, head_size=-1)
assert path == DiffusionAttentionBackendEnum.CUDNN_ATTN.get_path()
def test_explicit_flash_attn_rejects_blackwell_without_fa4(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
monkeypatch.setattr(PACKAGES_CHECKER, "get_packages_info", lambda: {"has_flash_attn": True})
with pytest.raises(ValueError, match="requires CuTe FlashAttention-4"):
CudaOmniPlatform.get_diffusion_attn_backend_cls("FLASH_ATTN", head_size=64)
def test_explicit_flash_attn_accepts_blackwell_when_fa4_available(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
monkeypatch.setattr(CudaOmniPlatform, "has_flash_attn_4", classmethod(lambda cls: True))
path = CudaOmniPlatform.get_diffusion_attn_backend_cls("FLASH_ATTN", head_size=64)
assert path == DiffusionAttentionBackendEnum.FLASH_ATTN.get_path()
def test_blackwell_dense_flash_capability_tracks_fa4(monkeypatch: pytest.MonkeyPatch):
_blackwell_sm120(monkeypatch)
assert CudaOmniPlatform.supports_diffusion_dense_flash_attention() is False
monkeypatch.setattr(CudaOmniPlatform, "has_flash_attn_4", classmethod(lambda cls: True))
assert CudaOmniPlatform.supports_diffusion_dense_flash_attention() is True
def test_hopper_dense_flash_capability_tracks_fa_package(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(
CudaOmniPlatform,
"get_device_capability",
classmethod(lambda cls, device_id=0: DeviceCapability(9, 0)),
)
monkeypatch.setattr(CudaOmniPlatform, "has_flash_attn_package", classmethod(lambda cls: False))
assert CudaOmniPlatform.supports_diffusion_dense_flash_attention() is False
monkeypatch.setattr(CudaOmniPlatform, "has_flash_attn_package", classmethod(lambda cls: True))
assert CudaOmniPlatform.supports_diffusion_dense_flash_attention() is True
def test_auto_selects_flash_attn_on_blackwell_when_cudnn_old_and_fa4_available(
monkeypatch: pytest.MonkeyPatch,
):
_blackwell_sm120(monkeypatch, cudnn_version=90499)
monkeypatch.setitem(sys.modules, "flashinfer", None)
monkeypatch.setattr(CudaOmniPlatform, "has_flash_attn_4", classmethod(lambda cls: True))
path = CudaOmniPlatform.get_diffusion_attn_backend_cls(None, head_size=128)
assert path == DiffusionAttentionBackendEnum.FLASH_ATTN.get_path()
def test_marked_paged_layer_constructs_on_blackwell_cudnn_without_fa4(monkeypatch: pytest.MonkeyPatch):
"""Scheduler-paged remap must not require dense FA4 when CUDNN is the default."""
_blackwell_sm120(monkeypatch)
_cached_get_backend_cls.cache_clear()
od_config = SimpleNamespace(
diffusion_attention_config=AttentionConfig(),
diffusion_kv_mode=DiffusionKVCacheMode.PAGED_SCHEDULER,
parallel_config=SimpleNamespace(ring_degree=1, allgather_degree=1),
diffusion_kv_cache_dtype=None,
diffusion_kv_cache_skip_step_indices=None,
diffusion_kv_cache_skip_layer_indices=None,
model_class_name=None,
)
with set_current_diffusion_config(od_config):
attention = Attention(
num_heads=4,
head_size=64,
causal=False,
softmax_scale=1.0,
paged_kv_cache_role="primary",
)
assert attention.attn_backend.get_name() == "FLASH_ATTN"
assert attention.backend_pref == "FLASH_ATTN"
assert attention.backend_explicit is False
assert attention.attn_spec is None