mirror of
https://github.com/vllm-project/vllm.git
synced 2026-08-10 07:48:09 +00:00
Signed-off-by: amitz-nv <[email protected]> Co-authored-by: OpenAI Codex <[email protected]>
189 lines
5.1 KiB
Python
189 lines
5.1 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
import torch
|
|
|
|
from vllm.config.mamba import MambaBackendEnum, MambaConfig, MambaSSUAlgorithm
|
|
from vllm.model_executor.layers.mamba.ops.ssu_dispatch import (
|
|
FlashInferSSUBackend,
|
|
TritonSSUBackend,
|
|
get_mamba_ssu_backend,
|
|
initialize_mamba_ssu_backend,
|
|
selective_state_update,
|
|
)
|
|
from vllm.utils.torch_utils import set_random_seed
|
|
from vllm.v1.attention.backends.registry import MambaAttentionBackendEnum
|
|
from vllm.v1.kv_cache_interface import (
|
|
KVCacheConfig,
|
|
KVCacheGroupSpec,
|
|
MambaSpec,
|
|
)
|
|
|
|
try:
|
|
import flashinfer.mamba # noqa: F401
|
|
|
|
HAS_FLASHINFER = True
|
|
except ImportError:
|
|
HAS_FLASHINFER = False
|
|
|
|
|
|
def _kv_cache_config_with_ssu(
|
|
mamba_type: MambaAttentionBackendEnum = MambaAttentionBackendEnum.MAMBA2,
|
|
) -> KVCacheConfig:
|
|
spec = MambaSpec(
|
|
block_size=16,
|
|
shapes=((16, 64),),
|
|
dtypes=(torch.float16,),
|
|
mamba_type=mamba_type,
|
|
)
|
|
return KVCacheConfig(
|
|
num_blocks=1,
|
|
kv_cache_tensors=[],
|
|
kv_cache_groups=[KVCacheGroupSpec(layer_names=["l0"], kv_cache_spec=spec)],
|
|
)
|
|
|
|
|
|
def test_default_backend_is_triton():
|
|
initialize_mamba_ssu_backend(MambaConfig(), _kv_cache_config_with_ssu())
|
|
backend = get_mamba_ssu_backend()
|
|
assert isinstance(backend, TritonSSUBackend)
|
|
assert backend.name == "triton"
|
|
|
|
|
|
def test_explicit_triton_backend():
|
|
initialize_mamba_ssu_backend(
|
|
MambaConfig(backend=MambaBackendEnum.TRITON), _kv_cache_config_with_ssu()
|
|
)
|
|
backend = get_mamba_ssu_backend()
|
|
assert isinstance(backend, TritonSSUBackend)
|
|
|
|
|
|
@pytest.mark.skipif(not HAS_FLASHINFER, reason="flashinfer not installed")
|
|
def test_flashinfer_backend_init():
|
|
initialize_mamba_ssu_backend(
|
|
MambaConfig(backend=MambaBackendEnum.FLASHINFER), _kv_cache_config_with_ssu()
|
|
)
|
|
backend = get_mamba_ssu_backend()
|
|
assert isinstance(backend, FlashInferSSUBackend)
|
|
assert backend.name == "flashinfer"
|
|
|
|
|
|
@pytest.mark.skipif(not HAS_FLASHINFER, reason="flashinfer not installed")
|
|
@pytest.mark.parametrize(
|
|
("algorithm", "expected"),
|
|
[
|
|
(None, "auto"),
|
|
("auto", "auto"),
|
|
("simple", "simple"),
|
|
("vertical", "vertical"),
|
|
("horizontal", "horizontal"),
|
|
],
|
|
)
|
|
def test_flashinfer_forwards_ssu_algorithm(
|
|
algorithm: MambaSSUAlgorithm | None,
|
|
expected: MambaSSUAlgorithm,
|
|
monkeypatch,
|
|
):
|
|
import flashinfer.mamba
|
|
|
|
kernel = Mock()
|
|
monkeypatch.setattr(flashinfer.mamba, "selective_state_update", kernel)
|
|
backend = FlashInferSSUBackend(
|
|
MambaConfig(
|
|
backend=MambaBackendEnum.FLASHINFER,
|
|
ssu_algorithm=algorithm,
|
|
)
|
|
)
|
|
|
|
tensor = torch.empty(1)
|
|
backend(
|
|
tensor,
|
|
tensor,
|
|
tensor,
|
|
tensor,
|
|
tensor,
|
|
tensor,
|
|
tensor,
|
|
tensor,
|
|
)
|
|
|
|
assert kernel.call_args.kwargs["algorithm"] == expected
|
|
|
|
|
|
def test_uninitialized_backend_raises():
|
|
import vllm.model_executor.layers.mamba.ops.ssu_dispatch as mod
|
|
|
|
old = mod._mamba_ssu_backend
|
|
mod._mamba_ssu_backend = None
|
|
with pytest.raises(RuntimeError, match="not been initialized"):
|
|
get_mamba_ssu_backend()
|
|
mod._mamba_ssu_backend = old
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"mamba_type",
|
|
[
|
|
MambaAttentionBackendEnum.LINEAR,
|
|
MambaAttentionBackendEnum.GDN_ATTN,
|
|
MambaAttentionBackendEnum.SHORT_CONV,
|
|
],
|
|
)
|
|
def test_init_is_noop_for_non_ssu_mamba_type(mamba_type):
|
|
import vllm.model_executor.layers.mamba.ops.ssu_dispatch as mod
|
|
|
|
old = mod._mamba_ssu_backend
|
|
mod._mamba_ssu_backend = None
|
|
try:
|
|
initialize_mamba_ssu_backend(
|
|
MambaConfig(), _kv_cache_config_with_ssu(mamba_type)
|
|
)
|
|
assert mod._mamba_ssu_backend is None
|
|
with pytest.raises(RuntimeError, match="not been initialized"):
|
|
get_mamba_ssu_backend()
|
|
finally:
|
|
mod._mamba_ssu_backend = old
|
|
|
|
|
|
@pytest.mark.skipif(HAS_FLASHINFER, reason="flashinfer is installed")
|
|
def test_flashinfer_import_error():
|
|
with pytest.raises(ImportError, match="FlashInfer is required"):
|
|
FlashInferSSUBackend(MambaConfig())
|
|
|
|
|
|
def test_triton_basic_call():
|
|
set_random_seed(0)
|
|
initialize_mamba_ssu_backend(
|
|
MambaConfig(backend=MambaBackendEnum.TRITON), _kv_cache_config_with_ssu()
|
|
)
|
|
device = "cuda"
|
|
batch_size = 2
|
|
dim = 64
|
|
dstate = 16
|
|
|
|
state = torch.randn(batch_size, dim, dstate, device=device)
|
|
x = torch.randn(batch_size, dim, device=device)
|
|
out = torch.empty_like(x)
|
|
dt = torch.randn(batch_size, dim, device=device)
|
|
dt_bias = torch.rand(dim, device=device) - 4.0
|
|
A = -torch.rand(dim, dstate, device=device)
|
|
B = torch.randn(batch_size, dstate, device=device)
|
|
C = torch.randn(batch_size, dstate, device=device)
|
|
D = torch.randn(dim, device=device)
|
|
|
|
selective_state_update(
|
|
state,
|
|
x,
|
|
dt,
|
|
A,
|
|
B,
|
|
C,
|
|
D=D,
|
|
dt_bias=dt_bias,
|
|
dt_softplus=True,
|
|
out=out,
|
|
)
|
|
assert not torch.isnan(out).any()
|