505 lines
22 KiB
Python
505 lines
22 KiB
Python
# Adapt from https://github.com/vllm-project/vllm/blob/main/vllm/v1/worker/gpu/attn_utils.py
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
# This file is a part of the vllm-ascend project.
|
|
#
|
|
|
|
from collections.abc import Sequence
|
|
from typing import Any
|
|
|
|
import numpy as np
|
|
import torch
|
|
from vllm.config import VllmConfig, get_current_vllm_config, get_layers_from_vllm_config
|
|
from vllm.model_executor.layers.attention import Attention
|
|
from vllm.model_executor.layers.attention.mla_attention import MLAAttention
|
|
from vllm.model_executor.layers.attention_layer_base import AttentionLayerBase
|
|
from vllm.v1.attention.backend import AttentionBackend
|
|
from vllm.v1.kv_cache_interface import (
|
|
AttentionSpec,
|
|
EncoderOnlyAttentionSpec,
|
|
KVCacheConfig,
|
|
KVCacheSpec,
|
|
MLAAttentionSpec,
|
|
UniformTypeKVCacheSpecs,
|
|
)
|
|
from vllm.v1.worker.gpu.model_states.interface import ModelSpecificAttnMetadata
|
|
from vllm.v1.worker.utils import AttentionGroup
|
|
|
|
from vllm_ascend.attention.attention_mask import AttentionMaskBuilder
|
|
from vllm_ascend.attention.attention_v1 import AscendAttentionState
|
|
from vllm_ascend.attention.utils import AscendCommonAttentionMetadata, AscendPrefillContextParallelMetadata
|
|
from vllm_ascend.core.kv_cache_interface import AscendMLAAttentionSpec
|
|
from vllm_ascend.quantization.utils import enable_fa_quant
|
|
from vllm_ascend.utils import calc_split_factor
|
|
|
|
_ATTENTION_MASK_BUILDER = None
|
|
|
|
|
|
def get_kv_cache_spec(vllm_config: VllmConfig) -> dict[str, KVCacheSpec]:
|
|
"""Build Ascend-specific KV cache specs for v2 worker patching."""
|
|
kv_cache_spec: dict[str, KVCacheSpec] = {}
|
|
layer_type = AttentionLayerBase
|
|
attn_layers = get_layers_from_vllm_config(vllm_config, layer_type)
|
|
|
|
for layer_name, attn_module in attn_layers.items():
|
|
if getattr(attn_module, "kv_sharing_target_layer_name", None):
|
|
continue
|
|
if isinstance(attn_module, Attention):
|
|
if spec := attn_module.get_kv_cache_spec(vllm_config):
|
|
kv_cache_spec[layer_name] = spec
|
|
continue
|
|
if isinstance(attn_module, MLAAttention):
|
|
spec = attn_module.get_kv_cache_spec(vllm_config)
|
|
if spec is None:
|
|
continue
|
|
if getattr(attn_module.impl, "fa_quant_layer", False):
|
|
head_size = attn_module.head_size + attn_module.qk_rope_head_dim
|
|
dtype, cache_dtype_str = attn_module.impl.dtype, None
|
|
else:
|
|
head_size = spec.head_size
|
|
dtype = spec.dtype
|
|
cache_dtype_str = spec.cache_dtype_str
|
|
kv_cache_spec[layer_name] = AscendMLAAttentionSpec(
|
|
block_size=spec.block_size,
|
|
num_kv_heads=spec.num_kv_heads,
|
|
head_size=head_size,
|
|
dtype=dtype,
|
|
cache_dtype_str=cache_dtype_str,
|
|
)
|
|
|
|
return kv_cache_spec
|
|
|
|
|
|
def get_attn_mask_builder(device: torch.device):
|
|
"""Get attention mask builder which only have one instance."""
|
|
global _ATTENTION_MASK_BUILDER
|
|
if _ATTENTION_MASK_BUILDER is None:
|
|
_ATTENTION_MASK_BUILDER = AttentionMaskBuilder(device)
|
|
return _ATTENTION_MASK_BUILDER
|
|
|
|
|
|
def build_attn_metadata(
|
|
*,
|
|
attn_groups: list[list[AttentionGroup]],
|
|
num_reqs: int,
|
|
num_tokens: int,
|
|
query_start_loc_gpu: torch.Tensor,
|
|
query_start_loc_cpu: torch.Tensor,
|
|
max_query_len: int,
|
|
seq_lens: torch.Tensor,
|
|
max_seq_len: int,
|
|
block_tables: Sequence[torch.Tensor],
|
|
slot_mappings: torch.Tensor,
|
|
kv_cache_config: KVCacheConfig,
|
|
dcp_local_seq_lens: torch.Tensor | None = None,
|
|
# extra attributes for ascend npus.
|
|
seq_lens_np: np.ndarray | None = None,
|
|
num_computed_tokens_cpu: torch.Tensor | None = None,
|
|
positions: torch.Tensor | None = None,
|
|
attn_state: Any | None = None,
|
|
graph_pad_size: int = -1,
|
|
num_input_tokens: int = 0,
|
|
prefill_context_parallel_metadata: AscendPrefillContextParallelMetadata | None = None,
|
|
model_specific_attn_metadata: ModelSpecificAttnMetadata | None = None,
|
|
for_cudagraph_capture: bool = False,
|
|
causal: bool = True,
|
|
) -> dict[str, Any]:
|
|
"""Build attention metadata for Ascend NPUs."""
|
|
# TODO(Ronald1995): optimize AscendCommonAttentionMetadata.
|
|
|
|
# seq_lens_np is used for ascend npus, it maybe None in spec_decode case,
|
|
# we fill it with max_seq_len in case `attn_metadata_builder.build` raise
|
|
# an error.
|
|
if seq_lens_np is None:
|
|
seq_lens_np = np.full(num_reqs, max_seq_len, dtype=np.int32)
|
|
seq_lens_cpu = torch.from_numpy(seq_lens_np)[:num_reqs]
|
|
|
|
attn_metadata: dict[str, Any] = {}
|
|
kv_cache_groups = kv_cache_config.kv_cache_groups
|
|
for i, kv_cache_spec in enumerate(kv_cache_groups):
|
|
block_table = block_tables[i]
|
|
slot_mapping = slot_mappings[i]
|
|
|
|
common_attn_metadata_extra_kwargs = (
|
|
model_specific_attn_metadata.get_extra_common_attn_kwargs(i, num_reqs)
|
|
if model_specific_attn_metadata is not None
|
|
else {}
|
|
)
|
|
common_attn_metadata = AscendCommonAttentionMetadata(
|
|
query_start_loc=query_start_loc_gpu,
|
|
query_start_loc_cpu=query_start_loc_cpu,
|
|
seq_lens_cpu=seq_lens_cpu,
|
|
seq_lens_cpu_upper_bound=seq_lens_cpu,
|
|
seq_lens=seq_lens[:num_reqs],
|
|
num_reqs=num_reqs,
|
|
num_actual_tokens=num_tokens,
|
|
max_query_len=max_query_len,
|
|
block_table_tensor=block_table,
|
|
slot_mapping=slot_mapping,
|
|
positions=positions,
|
|
attn_state=attn_state,
|
|
graph_pad_size=graph_pad_size,
|
|
num_input_tokens=num_input_tokens,
|
|
prefill_context_parallel_metadata=prefill_context_parallel_metadata,
|
|
max_seq_len=max_seq_len,
|
|
causal=causal,
|
|
**common_attn_metadata_extra_kwargs,
|
|
)
|
|
|
|
for attn_group in attn_groups[i]:
|
|
attn_metadata_builder = attn_group.get_metadata_builder(0)
|
|
if for_cudagraph_capture:
|
|
metadata = attn_metadata_builder.build_for_cudagraph_capture(common_attn_metadata)
|
|
else:
|
|
attn_metadata_extra_kwargs = (
|
|
model_specific_attn_metadata.get_extra_attn_kwargs(
|
|
attn_metadata_builder,
|
|
num_reqs,
|
|
)
|
|
if model_specific_attn_metadata is not None
|
|
else {}
|
|
)
|
|
metadata = attn_metadata_builder.build(
|
|
common_prefix_len=0,
|
|
common_attn_metadata=common_attn_metadata,
|
|
**attn_metadata_extra_kwargs,
|
|
)
|
|
for layer_name in attn_group.layer_names:
|
|
attn_metadata[layer_name] = metadata
|
|
return attn_metadata
|
|
|
|
|
|
def build_attn_state(
|
|
vllm_config: VllmConfig,
|
|
seq_lens_np: np.ndarray,
|
|
num_reqs,
|
|
num_scheduled_tokens,
|
|
num_valid_tokens,
|
|
):
|
|
"""Build attention state for npu's attention backend."""
|
|
if vllm_config.model_config.runner_type == "pooling":
|
|
if isinstance(
|
|
vllm_config.kv_cache_config.kv_cache_groups[0].kv_cache_spec,
|
|
EncoderOnlyAttentionSpec,
|
|
):
|
|
attn_state = AscendAttentionState.PrefillNoCache
|
|
else:
|
|
attn_state = AscendAttentionState.PrefillCacheHit
|
|
elif np.array_equal(seq_lens_np[:num_reqs], num_scheduled_tokens):
|
|
attn_state = AscendAttentionState.PrefillNoCache
|
|
# We assume it is the decode stage, where prefill occurs
|
|
# but only one token is not hit in cache.
|
|
elif np.all(num_scheduled_tokens == 1):
|
|
attn_state = AscendAttentionState.DecodeOnly
|
|
if vllm_config.speculative_config and vllm_config.speculative_config.method == "mtp":
|
|
# SpecDecoding now supports seq_len=1 and seq_len=2
|
|
# In Prefilling Decoding Disaggregation scenario, SpecDecoding
|
|
# need to supports seq_len=1
|
|
attn_state = AscendAttentionState.SpecDecoding
|
|
# Speculative decoding.
|
|
elif np.all(num_valid_tokens == 1):
|
|
if vllm_config.speculative_config and vllm_config.speculative_config.method == "mtp":
|
|
attn_state = AscendAttentionState.SpecDecoding
|
|
else:
|
|
attn_state = AscendAttentionState.ChunkedPrefill
|
|
# splitfuse
|
|
elif vllm_config.scheduler_config.enable_chunked_prefill:
|
|
attn_state = AscendAttentionState.ChunkedPrefill
|
|
else:
|
|
attn_state = AscendAttentionState.PrefillCacheHit
|
|
return attn_state
|
|
|
|
|
|
def _get_layer_kv_cache_specs(kv_cache_config: KVCacheConfig) -> dict[str, KVCacheSpec]:
|
|
layer_kv_cache_spec: dict[str, KVCacheSpec] = {}
|
|
for group_kv_cache_spec in kv_cache_config.kv_cache_groups:
|
|
group_spec = group_kv_cache_spec.kv_cache_spec
|
|
for layer_name in group_kv_cache_spec.layer_names:
|
|
if isinstance(group_spec, UniformTypeKVCacheSpecs):
|
|
layer_kv_cache_spec[layer_name] = group_spec.kv_cache_specs[layer_name]
|
|
else:
|
|
layer_kv_cache_spec[layer_name] = group_spec
|
|
return layer_kv_cache_spec
|
|
|
|
|
|
def _get_attention_kv_cache_dims(layer_name: str, kv_cache_spec: AttentionSpec) -> tuple[int, int]:
|
|
if isinstance(kv_cache_spec, AscendMLAAttentionSpec):
|
|
attn_layers = get_layers_from_vllm_config(get_current_vllm_config(), AttentionLayerBase, [layer_name])
|
|
attn_layer = attn_layers[layer_name]
|
|
if not isinstance(attn_layer, MLAAttention):
|
|
raise TypeError(f"Expected AscendMLAAttention layer for {layer_name}, got {type(attn_layer).__name__}.")
|
|
return attn_layer.kv_lora_rank, attn_layer.qk_rope_head_dim
|
|
|
|
head_size_v = kv_cache_spec.head_size_v if hasattr(kv_cache_spec, "head_size_v") else kv_cache_spec.head_size
|
|
return kv_cache_spec.head_size, head_size_v
|
|
|
|
|
|
def _align_memory(tensor: torch.Tensor, alignment: int) -> torch.Tensor:
|
|
data_ptr = tensor.data_ptr()
|
|
aligned_addr = (data_ptr + alignment - 1) // alignment * alignment
|
|
offset = (aligned_addr - data_ptr) // tensor.element_size()
|
|
return tensor[int(offset) :]
|
|
|
|
|
|
def _allocate_kv_cache(
|
|
kv_cache_config: KVCacheConfig,
|
|
shared_layers: dict[str, str],
|
|
device: torch.device,
|
|
) -> dict[str, tuple[torch.Tensor, torch.Tensor]]:
|
|
"""
|
|
Initialize the KV cache buffer with the correct size. The buffer needs to be
|
|
reshaped to the desired shape before being used by the models.
|
|
|
|
NOTE: To support prefill disaggregation, we need to split kvcache tensor
|
|
into k_cache and v_cache, and the addr of both are aligned by 2M.
|
|
|
|
Args:
|
|
kv_cache_config: The KV cache config
|
|
device: The device
|
|
Returns:
|
|
dict[str, tuple[torch.Tensor, torch.Tensor]]: A map between layer names
|
|
to their corresponding memory buffer for K cache and V cache
|
|
"""
|
|
vllm_config = get_current_vllm_config()
|
|
|
|
# init kv cache tensors
|
|
kv_cache_raw_tensors: dict[str, tuple[torch.Tensor, torch.Tensor]] = {}
|
|
# prefill disaggregation need the addr of cache tensor be aligned with 2M
|
|
alignment = 2 * 1024 * 1024
|
|
layer_kv_cache_spec = _get_layer_kv_cache_specs(kv_cache_config)
|
|
for kv_cache_tensor in kv_cache_config.kv_cache_tensors:
|
|
if len(kv_cache_tensor.shared_by) == 0:
|
|
continue
|
|
|
|
# NOTE: We need to init k_cache tensor (nope cache tensor in mla) and
|
|
# v_cache tensor (rope cache tensor in mla) separately to support
|
|
# prefill disaggregation, as it only supports the 0-dim of kv_cache is
|
|
# `num_blocks`.
|
|
# For deepseek mla, we need to spilt cache tensor accrodding to the nope
|
|
# head dim and rope head dim.
|
|
example_layer_name = kv_cache_tensor.shared_by[0]
|
|
example_kv_cache_spec = layer_kv_cache_spec[example_layer_name]
|
|
assert isinstance(example_kv_cache_spec, AttentionSpec)
|
|
|
|
k_dim, v_dim = _get_attention_kv_cache_dims(example_layer_name, example_kv_cache_spec)
|
|
assert k_dim > 0 and v_dim > 0
|
|
kv_head_dim_list = [k_dim, v_dim]
|
|
if enable_fa_quant(vllm_config):
|
|
k_tensor_split_factor, v_tensor_split_factor = vllm_config.quant_config.get_kv_quant_split_factor(
|
|
example_layer_name, kv_head_dim_list
|
|
)
|
|
else:
|
|
k_tensor_split_factor, v_tensor_split_factor = calc_split_factor(kv_head_dim_list)
|
|
k_tensor_size = int(kv_cache_tensor.size // k_tensor_split_factor)
|
|
v_tensor_size = int(kv_cache_tensor.size // v_tensor_split_factor)
|
|
|
|
if vllm_config.kv_transfer_config is None:
|
|
k_tensor = torch.zeros(k_tensor_size, dtype=torch.int8, device=device)
|
|
v_tensor = torch.zeros(v_tensor_size, dtype=torch.int8, device=device)
|
|
else:
|
|
k_tensor = torch.zeros(k_tensor_size + alignment, dtype=torch.int8, device=device)
|
|
v_tensor = torch.zeros(v_tensor_size + alignment, dtype=torch.int8, device=device)
|
|
k_tensor = _align_memory(k_tensor, alignment)[:k_tensor_size]
|
|
v_tensor = _align_memory(v_tensor, alignment)[:v_tensor_size]
|
|
for layer_name in kv_cache_tensor.shared_by:
|
|
kv_cache_raw_tensors[layer_name] = (k_tensor, v_tensor)
|
|
|
|
layer_names = set()
|
|
for group in kv_cache_config.kv_cache_groups:
|
|
for layer_name in group.layer_names:
|
|
layer_names.add(layer_name)
|
|
assert layer_names == (kv_cache_raw_tensors.keys() | shared_layers.keys()), (
|
|
"Some layers are not correctly initialized"
|
|
)
|
|
|
|
return kv_cache_raw_tensors
|
|
|
|
|
|
def _reshape_kv_cache(
|
|
kv_cache_config: KVCacheConfig,
|
|
kv_cache_raw_tensors: dict[str, tuple[torch.Tensor, torch.Tensor]],
|
|
attn_backends: dict[str, AttentionBackend],
|
|
cache_dtype: str,
|
|
kernel_block_sizes: list[int] | None = None,
|
|
shared_kv_cache_layers: dict[str, str] | None = None,
|
|
) -> dict[str, tuple[torch.Tensor, torch.Tensor]]:
|
|
"""
|
|
Reshape the KV cache tensors to the desired shape and dtype.
|
|
|
|
Args:
|
|
kv_cache_config: The KV cache config
|
|
kv_cache_raw_tensors: The KV cache buffer of each layer, with correct
|
|
size but uninitialized shape
|
|
Returns:
|
|
dict[str, tuple[torch.Tensor, torch.Tensor]]: A map between layer names
|
|
to their corresponding memory buffer for KV cache
|
|
"""
|
|
vllm_config = get_current_vllm_config()
|
|
|
|
kv_caches: dict[str, tuple[torch.Tensor, torch.Tensor]] = {}
|
|
kernel_block_sizes = kernel_block_sizes or []
|
|
for kv_cache_group_id, kv_cache_group_spec in enumerate(kv_cache_config.kv_cache_groups):
|
|
for layer_name in kv_cache_group_spec.layer_names:
|
|
if shared_kv_cache_layers and layer_name in shared_kv_cache_layers:
|
|
continue
|
|
kv_cache_spec = kv_cache_group_spec.kv_cache_spec
|
|
if isinstance(kv_cache_spec, UniformTypeKVCacheSpecs):
|
|
kv_cache_spec = kv_cache_spec.kv_cache_specs[layer_name]
|
|
assert isinstance(kv_cache_spec, AttentionSpec)
|
|
|
|
if isinstance(kv_cache_spec, AttentionSpec):
|
|
raw_k_tensor, raw_v_tensor = kv_cache_raw_tensors[layer_name]
|
|
assert raw_k_tensor is not None
|
|
assert raw_v_tensor is not None
|
|
sum_page_size_bytes = raw_k_tensor.numel() + raw_v_tensor.numel()
|
|
assert sum_page_size_bytes % kv_cache_spec.page_size_bytes == 0
|
|
num_blocks = sum_page_size_bytes // kv_cache_spec.page_size_bytes
|
|
|
|
# `num_blocks` is the number of blocks the model runner can use.
|
|
# `kv_cache_config.num_blocks` is the number of blocks that
|
|
# KVCacheManager may allocate.
|
|
# Since different GPUs may have different number of layers and
|
|
# different memory capacities, `num_blocks` can be different on
|
|
# different GPUs, and `kv_cache_config.num_blocks` is set to
|
|
# the min of all `num_blocks`. Verify it here.
|
|
assert num_blocks >= kv_cache_config.num_blocks
|
|
|
|
attn_backend = attn_backends[layer_name]
|
|
if kv_cache_group_id < len(kernel_block_sizes):
|
|
kernel_block_size = kernel_block_sizes[kv_cache_group_id]
|
|
num_blocks *= kv_cache_spec.block_size // kernel_block_size
|
|
else:
|
|
kernel_block_size = kv_cache_spec.block_size
|
|
|
|
if kv_cache_spec.storage_block_size != kv_cache_spec.block_size:
|
|
shape_block_size = kv_cache_spec.storage_block_size
|
|
else:
|
|
shape_block_size = kernel_block_size
|
|
|
|
kv_cache_shape = attn_backend.get_kv_cache_shape(
|
|
num_blocks,
|
|
shape_block_size,
|
|
kv_cache_spec.num_kv_heads,
|
|
kv_cache_spec.head_size,
|
|
cache_dtype,
|
|
)
|
|
if not isinstance(kv_cache_spec, AscendMLAAttentionSpec):
|
|
k_shape = kv_cache_shape[1:]
|
|
if hasattr(kv_cache_spec, "head_size_v"):
|
|
v_shape = (*kv_cache_shape[1:-1], kv_cache_spec.head_size_v)
|
|
else:
|
|
v_shape = k_shape
|
|
else:
|
|
# k_cache: nope_cache v_cache: rope_cache
|
|
mla_num_blocks, mla_block_size, num_kv_heads, _ = kv_cache_shape
|
|
k_dim, v_dim = _get_attention_kv_cache_dims(layer_name, kv_cache_spec)
|
|
k_shape = (mla_num_blocks, mla_block_size, num_kv_heads, k_dim)
|
|
v_shape = (mla_num_blocks, mla_block_size, num_kv_heads, v_dim)
|
|
|
|
k_cache_dtype = v_cache_dtype = kv_cache_spec.dtype
|
|
if enable_fa_quant(vllm_config):
|
|
k_cache_dtype, v_cache_dtype = vllm_config.quant_config.get_kv_quant_dtype(
|
|
layer_name, kv_cache_spec.dtype, vllm_config.model_config
|
|
)
|
|
|
|
k_cache = raw_k_tensor.view(k_cache_dtype).view(k_shape)
|
|
v_cache = raw_v_tensor.view(v_cache_dtype).view(v_shape)
|
|
kv_caches[layer_name] = (k_cache, v_cache)
|
|
else:
|
|
raise ValueError("Unknown KV cache spec type.")
|
|
|
|
if shared_kv_cache_layers:
|
|
for layer_name, target_layer_name in shared_kv_cache_layers.items():
|
|
kv_caches[layer_name] = kv_caches[target_layer_name]
|
|
|
|
return kv_caches
|
|
|
|
|
|
def _reshape_kv_cache_v2(
|
|
attn_groups: Sequence[AttentionGroup],
|
|
kv_cache_raw_tensors: dict[str, tuple[torch.Tensor, torch.Tensor]],
|
|
cache_dtype: str,
|
|
kernel_block_sizes: list[int],
|
|
shared_kv_cache_layers: dict[str, str],
|
|
kv_cache_config: "KVCacheConfig | None" = None,
|
|
) -> dict[str, tuple[torch.Tensor, torch.Tensor]]:
|
|
vllm_config = get_current_vllm_config()
|
|
is_kv_consumer = (
|
|
vllm_config.kv_transfer_config.is_kv_consumer if vllm_config.kv_transfer_config is not None else False
|
|
)
|
|
|
|
kv_caches: dict[str, tuple[torch.Tensor, torch.Tensor]] = {}
|
|
for group in attn_groups:
|
|
if group.kv_cache_group_id >= len(kernel_block_sizes):
|
|
continue
|
|
|
|
kv_cache_spec = group.kv_cache_spec
|
|
if kv_cache_spec.storage_block_size != kv_cache_spec.block_size:
|
|
kernel_block_size = kv_cache_spec.storage_block_size
|
|
else:
|
|
kernel_block_size = kernel_block_sizes[group.kv_cache_group_id]
|
|
|
|
for layer_name in group.layer_names:
|
|
if layer_name in shared_kv_cache_layers:
|
|
continue
|
|
|
|
assert isinstance(kv_cache_spec, AttentionSpec)
|
|
|
|
raw_k_tensor, raw_v_tensor = kv_cache_raw_tensors[layer_name]
|
|
assert raw_k_tensor is not None
|
|
assert raw_v_tensor is not None
|
|
sum_page_size_bytes = raw_k_tensor.numel() + raw_v_tensor.numel()
|
|
assert sum_page_size_bytes % kv_cache_spec.page_size_bytes == 0
|
|
num_blocks = sum_page_size_bytes // kv_cache_spec.page_size_bytes
|
|
|
|
num_blocks_per_kv_block = kv_cache_spec.block_size // kernel_block_size
|
|
kernel_num_blocks = num_blocks * num_blocks_per_kv_block
|
|
|
|
kv_cache_shape = group.backend.get_kv_cache_shape(
|
|
kernel_num_blocks,
|
|
kernel_block_size,
|
|
kv_cache_spec.num_kv_heads,
|
|
kv_cache_spec.head_size,
|
|
cache_dtype,
|
|
)
|
|
|
|
if not isinstance(kv_cache_spec, (AscendMLAAttentionSpec, MLAAttentionSpec)):
|
|
k_shape = kv_cache_shape[1:]
|
|
if hasattr(kv_cache_spec, "head_size_v"):
|
|
v_shape = (*kv_cache_shape[1:-1], kv_cache_spec.head_size_v)
|
|
else:
|
|
v_shape = k_shape
|
|
else:
|
|
mla_num_blocks, mla_block_size, num_kv_heads, _ = kv_cache_shape
|
|
k_dim, v_dim = _get_attention_kv_cache_dims(layer_name, kv_cache_spec)
|
|
k_shape = (mla_num_blocks, mla_block_size, num_kv_heads, k_dim)
|
|
v_shape = (mla_num_blocks, mla_block_size, num_kv_heads, v_dim)
|
|
|
|
k_cache_dtype = v_cache_dtype = kv_cache_spec.dtype
|
|
if is_kv_consumer and enable_fa_quant(vllm_config):
|
|
k_cache_dtype, v_cache_dtype = vllm_config.quant_config.get_kv_quant_dtype(
|
|
layer_name, kv_cache_spec.dtype, vllm_config.model_config
|
|
)
|
|
|
|
k_cache = raw_k_tensor.view(k_cache_dtype).view(k_shape)
|
|
v_cache = raw_v_tensor.view(v_cache_dtype).view(v_shape)
|
|
kv_caches[layer_name] = (k_cache, v_cache)
|
|
|
|
for layer_name, target_layer_name in shared_kv_cache_layers.items():
|
|
kv_caches[layer_name] = kv_caches[target_layer_name]
|
|
|
|
return kv_caches
|