243 lines
6.6 KiB
Python
243 lines
6.6 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
import torch
|
|
|
|
from vllm.v1.kv_cache_interface import FullAttentionSpec, KVQuantMode
|
|
from vllm.v1.worker.gpu.attn_utils import _reshape_kv_cache
|
|
from vllm.v1.worker.utils import AttentionGroup
|
|
|
|
|
|
class FakeFlashAttentionBackend:
|
|
@staticmethod
|
|
def get_kv_cache_shape(
|
|
num_blocks: int,
|
|
block_size: int,
|
|
num_kv_heads: int,
|
|
head_size: int,
|
|
cache_dtype_str: str = "auto",
|
|
) -> tuple[int, ...]:
|
|
return (num_blocks, 2, block_size, num_kv_heads, head_size)
|
|
|
|
@staticmethod
|
|
def get_kv_cache_stride_order(
|
|
include_num_layers_dimension: bool = False,
|
|
) -> tuple[int, ...]:
|
|
assert not include_num_layers_dimension
|
|
return (0, 1, 2, 3, 4)
|
|
|
|
|
|
class FakeHNDFlashAttentionBackend(FakeFlashAttentionBackend):
|
|
@staticmethod
|
|
def get_kv_cache_stride_order(
|
|
include_num_layers_dimension: bool = False,
|
|
) -> tuple[int, ...]:
|
|
assert not include_num_layers_dimension
|
|
return (0, 1, 3, 2, 4)
|
|
|
|
|
|
def test_reshape_padded_flash_attention_kv_cache_strides_by_page():
|
|
num_blocks = 3
|
|
spec = FullAttentionSpec(
|
|
block_size=16,
|
|
num_kv_heads=1,
|
|
head_size=2,
|
|
dtype=torch.float32,
|
|
page_size_padded=384,
|
|
)
|
|
assert spec.real_page_size_bytes == 256
|
|
|
|
raw_tensors = {
|
|
"layer": torch.zeros(spec.page_size_bytes * num_blocks, dtype=torch.int8)
|
|
}
|
|
attn_groups = [
|
|
AttentionGroup(
|
|
backend=FakeFlashAttentionBackend,
|
|
layer_names=["layer"],
|
|
kv_cache_spec=spec,
|
|
kv_cache_group_id=0,
|
|
)
|
|
]
|
|
|
|
kv_cache = _reshape_kv_cache(
|
|
attn_groups,
|
|
raw_tensors,
|
|
"auto",
|
|
[spec.block_size],
|
|
{},
|
|
)["layer"]
|
|
|
|
assert kv_cache.shape == (num_blocks, 2, 16, 1, 2)
|
|
assert kv_cache.stride(0) == spec.page_size_bytes // 4
|
|
assert kv_cache.stride(1) == spec.real_page_size_bytes // 2 // 4
|
|
assert kv_cache[1, 0].storage_offset() == spec.page_size_bytes // 4
|
|
assert (
|
|
kv_cache[1, 1].storage_offset()
|
|
== (spec.page_size_bytes + spec.real_page_size_bytes // 2) // 4
|
|
)
|
|
|
|
|
|
def test_reshape_padded_hnd_flash_attention_kv_cache_strides_by_page():
|
|
num_blocks = 3
|
|
spec = FullAttentionSpec(
|
|
block_size=16,
|
|
num_kv_heads=3,
|
|
head_size=2,
|
|
dtype=torch.float32,
|
|
page_size_padded=1024,
|
|
)
|
|
assert spec.real_page_size_bytes == 768
|
|
|
|
raw_tensors = {
|
|
"layer": torch.zeros(spec.page_size_bytes * num_blocks, dtype=torch.int8)
|
|
}
|
|
attn_groups = [
|
|
AttentionGroup(
|
|
backend=FakeHNDFlashAttentionBackend,
|
|
layer_names=["layer"],
|
|
kv_cache_spec=spec,
|
|
kv_cache_group_id=0,
|
|
)
|
|
]
|
|
|
|
kv_cache = _reshape_kv_cache(
|
|
attn_groups,
|
|
raw_tensors,
|
|
"auto",
|
|
[spec.block_size],
|
|
{},
|
|
)["layer"]
|
|
|
|
assert kv_cache.shape == (num_blocks, 2, 16, 3, 2)
|
|
assert kv_cache.stride(0) == spec.page_size_bytes // 4
|
|
assert kv_cache.stride(1) == spec.real_page_size_bytes // 2 // 4
|
|
assert kv_cache.stride(2) == 2
|
|
assert kv_cache.stride(3) == spec.block_size * spec.head_size
|
|
assert kv_cache[1, 0].storage_offset() == spec.page_size_bytes // 4
|
|
assert (
|
|
kv_cache[1, 1].storage_offset()
|
|
== (spec.page_size_bytes + spec.real_page_size_bytes // 2) // 4
|
|
)
|
|
assert (
|
|
kv_cache[1, 1, 3, 2].storage_offset()
|
|
== (
|
|
spec.page_size_bytes
|
|
+ spec.real_page_size_bytes // 2
|
|
+ 3 * spec.head_size * 4
|
|
+ 2 * spec.block_size * spec.head_size * 4
|
|
)
|
|
// 4
|
|
)
|
|
|
|
|
|
class FakeDiffKVBackend:
|
|
@staticmethod
|
|
def get_kv_cache_shape(
|
|
num_blocks: int,
|
|
block_size: int,
|
|
num_kv_heads: int,
|
|
head_size: int,
|
|
cache_dtype_str: str = "auto",
|
|
) -> tuple[int, ...]:
|
|
return (num_blocks, block_size, num_kv_heads, head_size * 2)
|
|
|
|
@staticmethod
|
|
def get_kv_cache_stride_order(
|
|
include_num_layers_dimension: bool = False,
|
|
) -> tuple[int, ...]:
|
|
assert not include_num_layers_dimension
|
|
return (0, 1, 2, 3)
|
|
|
|
|
|
def test_reshape_padded_diff_kv_cache_does_not_infer_kv_dim():
|
|
num_blocks = 3
|
|
spec = FullAttentionSpec(
|
|
block_size=16,
|
|
num_kv_heads=1,
|
|
head_size=2,
|
|
dtype=torch.float32,
|
|
page_size_padded=384,
|
|
)
|
|
|
|
raw_tensors = {
|
|
"layer": torch.zeros(spec.page_size_bytes * num_blocks, dtype=torch.int8)
|
|
}
|
|
attn_groups = [
|
|
AttentionGroup(
|
|
backend=FakeDiffKVBackend,
|
|
layer_names=["layer"],
|
|
kv_cache_spec=spec,
|
|
kv_cache_group_id=0,
|
|
)
|
|
]
|
|
|
|
kv_cache = _reshape_kv_cache(
|
|
attn_groups,
|
|
raw_tensors,
|
|
"auto",
|
|
[spec.block_size],
|
|
{},
|
|
)["layer"]
|
|
|
|
assert kv_cache.shape == (num_blocks, 16, 1, 4)
|
|
assert kv_cache.stride(0) == spec.page_size_bytes // 4
|
|
assert kv_cache.stride(1) == 4
|
|
|
|
|
|
class FakePerTokenScaleBackend:
|
|
@staticmethod
|
|
def get_kv_cache_shape(
|
|
num_blocks: int,
|
|
block_size: int,
|
|
num_kv_heads: int,
|
|
head_size: int,
|
|
cache_dtype_str: str = "auto",
|
|
) -> tuple[int, ...]:
|
|
return (num_blocks, 2, block_size, num_kv_heads, head_size + 4)
|
|
|
|
@staticmethod
|
|
def get_kv_cache_stride_order(
|
|
include_num_layers_dimension: bool = False,
|
|
) -> tuple[int, ...]:
|
|
assert not include_num_layers_dimension
|
|
return (0, 1, 2, 3, 4)
|
|
|
|
|
|
def test_reshape_padded_quantized_kv_cache_preserves_scale_stride():
|
|
num_blocks = 3
|
|
spec = FullAttentionSpec(
|
|
block_size=16,
|
|
num_kv_heads=1,
|
|
head_size=4,
|
|
dtype=torch.int8,
|
|
kv_quant_mode=KVQuantMode.INT8_PER_TOKEN_HEAD,
|
|
page_size_padded=384,
|
|
)
|
|
assert spec.real_page_size_bytes == 128
|
|
assert spec.page_size_bytes == 384
|
|
|
|
raw_tensors = {
|
|
"layer": torch.zeros(spec.page_size_bytes * num_blocks, dtype=torch.int8)
|
|
}
|
|
attn_groups = [
|
|
AttentionGroup(
|
|
backend=FakePerTokenScaleBackend,
|
|
layer_names=["layer"],
|
|
kv_cache_spec=spec,
|
|
kv_cache_group_id=0,
|
|
)
|
|
]
|
|
|
|
kv_cache = _reshape_kv_cache(
|
|
attn_groups,
|
|
raw_tensors,
|
|
"int8_per_token_head",
|
|
[spec.block_size],
|
|
{},
|
|
)["layer"]
|
|
|
|
assert kv_cache.shape == (num_blocks, 2, 16, 1, 8)
|
|
assert kv_cache.stride(0) == spec.page_size_bytes
|
|
assert kv_cache.stride(1) == 16 * 1 * 8
|
|
assert kv_cache[1, 1].storage_offset() == spec.page_size_bytes + 16 * 1 * 8
|