Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f4e862a
Fix kvcache allocate for different target/draft models.
SidaoY Mar 16, 2026
7bfa58c
Fix conflict.
SidaoY Mar 16, 2026
d2f09d5
Fix0.
SidaoY Mar 16, 2026
2f78072
Add kv dim validation.
SidaoY Mar 16, 2026
711eef1
Fix1.
SidaoY Mar 17, 2026
70be7a2
Fix conflict.
SidaoY Mar 17, 2026
66b6379
Fix for lint.
SidaoY Mar 17, 2026
bf4aaa5
Fix for old version.
SidaoY Mar 17, 2026
849a165
Fix review comments.
SidaoY Mar 17, 2026
325decb
Fix for lint.
SidaoY Mar 17, 2026
36716d6
Add e2e test for mla target and gqa draft.
SidaoY Mar 17, 2026
0529115
Merge branch 'main' into draft_kv
SidaoY Mar 17, 2026
5d05577
Fix for CI.
SidaoY Mar 17, 2026
15771f9
Fix for CI.
SidaoY Mar 18, 2026
e97bfe1
Merge branch 'main' into draft_kv
SidaoY Mar 18, 2026
255e5d5
Fix lint.
SidaoY Mar 18, 2026
1672e46
Merge branch 'main' into draft_kv
SidaoY Mar 18, 2026
40a6b2c
Fix CI.
SidaoY Mar 18, 2026
62bf558
Fix CI.
SidaoY Mar 19, 2026
9f1e405
Remove inappropriate e2e test cases, replace with module unit tests.
SidaoY Mar 19, 2026
a7491d6
Merge branch 'main' into draft_kv
SidaoY Mar 19, 2026
5dda255
Fix for ut.
SidaoY Mar 19, 2026
74fed00
Merge branch 'main' into draft_kv
SidaoY Mar 19, 2026
65e8b2f
Merge branch 'main' into draft_kv
SidaoY Mar 19, 2026
86e16ac
Merge branch 'main' into draft_kv
SidaoY Mar 19, 2026
c6797b3
Merge branch 'main' into draft_kv
SidaoY Mar 19, 2026
cef2642
Fix.
SidaoY Mar 20, 2026
4bad3b0
Merge branch 'main' into draft_kv
SidaoY Mar 20, 2026
f951b05
Merge branch 'main' into draft_kv
SidaoY Mar 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions vllm_ascend/spec_decode/eagle_proposer.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ def load_model(self, model: nn.Module) -> None:
self.model.config.image_token_index = model.config.image_token_id
elif self.get_model_name(model) == "PixtralForConditionalGeneration":
self.model.config.image_token_index = model.config.vision_config.image_token_id
elif self.get_model_name(model) == "KimiK25ForConditionalGeneration":
self.model.config.image_token_index = model.config.media_placeholder_token_id
else:
self.model.config.image_token_index = model.config.image_token_index
target_language_model = model.get_language_model()
Expand Down Expand Up @@ -388,7 +390,10 @@ def dummy_run(
: num_reqs * self.decode_threshold
]

builder = self.runner.attn_groups[0][0].get_metadata_builder()
if hasattr(self, "draft_attn_groups") and len(self.draft_attn_groups) > 0:
Comment thread
SidaoY marked this conversation as resolved.
Outdated
builder = self.draft_attn_groups[0].get_metadata_builder()
else:
builder = self.runner.attn_groups[0][0].get_metadata_builder()
# update the tensor's address for each step.
for draft_step in range(self.num_speculative_tokens):
common_attn_metadata = self.shallow_copy_metadata(common_attn_metadata)
Expand Down Expand Up @@ -600,7 +605,10 @@ def _propose(
common_attn_metadata.slot_mapping = self.slot_mapping_group[0]
common_attn_metadata.num_input_tokens = num_input_tokens
# FIXME(woosuk): The below two ops cause synchronization. Optimize.
builder = self.runner.attn_groups[0][0].get_metadata_builder()
if hasattr(self, "draft_attn_groups") and len(self.draft_attn_groups) > 0:
builder = self.draft_attn_groups[0].get_metadata_builder()
else:
builder = self.runner.attn_groups[0][0].get_metadata_builder()
attn_metadata = builder.build(0, common_attn_metadata, self.runner.get_model())

if self.uses_mrope:
Expand Down
121 changes: 75 additions & 46 deletions vllm_ascend/worker/model_runner_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -2626,6 +2626,34 @@ def initialize_kv_cache_tensors(self, kv_cache_config: KVCacheConfig) -> dict[st
bind_kv_cache(kv_caches, self.compilation_config.static_forward_context, self.kv_caches, num_attn_module)
return kv_caches

def _get_layer_kv_cache_specs(self, 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(self, layer_name: str, kv_cache_spec: AttentionSpec) -> tuple[int, int]:
if isinstance(kv_cache_spec, MLAAttentionSpec):
attn_layers = get_layers_from_vllm_config(
self.vllm_config,
AttentionLayerBase,
[layer_name],
)
attn_layer = attn_layers[layer_name]
if not isinstance(attn_layer, MLAAttention):
raise TypeError(
f"Expected MLAAttention 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 _allocate_kv_cache_tensors(self, kv_cache_config: KVCacheConfig) -> dict[str, torch.Tensor]:
"""
Initializes the KV cache buffer with the correct size. The buffer needs
Expand All @@ -2646,10 +2674,7 @@ def _allocate_kv_cache_tensors(self, kv_cache_config: KVCacheConfig) -> dict[str
kv_cache_raw_tensors: dict[str, torch.Tensor | torch.Tensor | None | None] = {}
# prefill disaggregation need the addr of cache tensor be aligned with 2M
alignment = 2 * 1024 * 1024
layer_kv_cache_spec: dict[str, KVCacheSpec] = {}
for group_kv_cache_spec in kv_cache_config.kv_cache_groups:
for layer_name in group_kv_cache_spec.layer_names:
layer_kv_cache_spec[layer_name] = group_kv_cache_spec.kv_cache_spec
layer_kv_cache_spec = self._get_layer_kv_cache_specs(kv_cache_config)
# If some tensors are shared by linear layers and attention layers,
# the same tensor format must be maintained even if some layers
# have only linear or attention layers, for example, the mtp layer.
Expand Down Expand Up @@ -2684,11 +2709,10 @@ def _allocate_kv_cache_tensors(self, kv_cache_config: KVCacheConfig) -> dict[str
# as it only support 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.
if not self.model_config.use_mla:
# for non-mla model, use FullAttentionSpec
k_tensor_split_factor = 2.0
v_tensor_split_factor = 2.0
elif self.use_sparse:
current_kv_cache_spec = layer_kv_cache_spec[layer_name]
assert isinstance(current_kv_cache_spec, AttentionSpec)

if self.use_sparse:
# for deepseek v3.2, we split the kv cache according to the corresponding ratio
kv_cache_spec = layer_kv_cache_spec[layer_name]
sparse_kv_cache_ratio = kv_cache_spec.sparse_kv_cache_ratio
Expand All @@ -2697,10 +2721,11 @@ def _allocate_kv_cache_tensors(self, kv_cache_config: KVCacheConfig) -> dict[str
dsa_k_tensor_split_factor = sparse_kv_cache_ratio[2]
dsa_k_scale_tensor_split_factor = sparse_kv_cache_ratio[3]
else:
# for other deepseek models, use MLAAttentionSpec
k_dim, v_dim = self._get_attention_kv_cache_dims(layer_name, current_kv_cache_spec)
assert k_dim > 0 and v_dim > 0
kv_head_dim_list = [
self.model_config.hf_text_config.kv_lora_rank,
self.model_config.hf_text_config.qk_rope_head_dim,
k_dim,
v_dim,
]
if self.is_kv_consumer and self.vllm_config.quant_config is not None:
k_tensor_split_factor, v_tensor_split_factor = (
Expand Down Expand Up @@ -2788,20 +2813,18 @@ def _reshape_kv_cache_tensors(
corresponding memory buffer for KV cache.
"""
kv_caches: dict[str, torch.Tensor] = {}
layer_kv_cache_spec = {}
for group in kv_cache_config.kv_cache_groups:
for layer_name in group.layer_names:
layer_kv_cache_spec[layer_name] = group.kv_cache_spec
layer_kv_cache_spec = self._get_layer_kv_cache_specs(kv_cache_config)
for group in self._kv_cache_spec_attn_group_iterator():
kv_cache_spec = group.kv_cache_spec
attn_backend = group.backend
for layer_name in group.layer_names:
if layer_name in self.runner_only_attn_layers:
continue

current_kv_cache_spec = layer_kv_cache_spec[layer_name]

# TODO: remove this after the OOM issue is located and fixed, otherwise, some model may
# encounter OOM issue
if isinstance(kv_cache_spec, AttentionSpec):
if isinstance(current_kv_cache_spec, AttentionSpec):
if self.use_sparse:
if self.use_sparse_c8_indexer:
raw_k_tensor, raw_v_tensor, raw_dsa_k_tensor, raw_dsa_k_scale_tensor = kv_cache_raw_tensors[ # type: ignore
Expand Down Expand Up @@ -2831,8 +2854,8 @@ def _reshape_kv_cache_tensors(
sum_page_size_bytes = raw_k_tensor.numel() + raw_v_tensor.numel()
assert raw_k_tensor is not None
assert raw_v_tensor is not None
assert sum_page_size_bytes % kv_cache_spec.page_size_bytes == 0
num_blocks = sum_page_size_bytes // kv_cache_spec.page_size_bytes
assert sum_page_size_bytes % current_kv_cache_spec.page_size_bytes == 0
num_blocks = sum_page_size_bytes // current_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
Expand All @@ -2846,57 +2869,63 @@ def _reshape_kv_cache_tensors(
if hasattr(attn_backend, "get_supported_kernel_block_sizes") and self.use_hybrid_blocks:
block_size = attn_backend.get_supported_kernel_block_sizes()[0]

block_size_chunk = kv_cache_spec.block_size // block_size
block_size_chunk = current_kv_cache_spec.block_size // block_size
kv_cache_shape = attn_backend.get_kv_cache_shape(
num_blocks * block_size_chunk,
block_size,
kv_cache_spec.num_kv_heads,
kv_cache_spec.head_size,
current_kv_cache_spec.num_kv_heads,
current_kv_cache_spec.head_size,
)
if self.hybrid_with_attn_and_mamba:
attn_tensor_page_size = int(np.prod(kv_cache_shape[1:])) * get_dtype_size(
kv_cache_spec.dtype
current_kv_cache_spec.dtype
)
conv_block_padding_size = raw_k_tensor.numel() - attn_tensor_page_size * 2
raw_kv_tensor = raw_k_tensor[conv_block_padding_size:]
raw_k_tensor = raw_kv_tensor[:attn_tensor_page_size]
raw_v_tensor = raw_kv_tensor[attn_tensor_page_size:]
else:
kv_cache_shape = self.attn_backend.get_kv_cache_shape(
num_blocks, kv_cache_spec.block_size, kv_cache_spec.num_kv_heads, kv_cache_spec.head_size
kv_cache_shape = attn_backend.get_kv_cache_shape(
num_blocks,
current_kv_cache_spec.block_size,
current_kv_cache_spec.num_kv_heads,
current_kv_cache_spec.head_size,
)

if not self.model_config.use_mla:
if not isinstance(current_kv_cache_spec, MLAAttentionSpec):
k_shape = kv_cache_shape[1:]
v_shape = k_shape
if hasattr(current_kv_cache_spec, "head_size_v"):
v_shape = (*kv_cache_shape[1:-1], current_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_shape = [
k_dim, v_dim = self._get_attention_kv_cache_dims(layer_name, current_kv_cache_spec)
k_shape = (
mla_num_blocks,
mla_block_size,
num_kv_heads,
self.model_config.hf_text_config.kv_lora_rank,
]
v_shape = [
k_dim,
)
v_shape = (
mla_num_blocks,
mla_block_size,
num_kv_heads,
self.model_config.hf_text_config.qk_rope_head_dim,
]
k_cache_dtype = v_cache_dtype = kv_cache_spec.dtype
v_dim,
)
k_cache_dtype = v_cache_dtype = current_kv_cache_spec.dtype
if self.is_kv_consumer and self.vllm_config.quant_config is not None:
k_cache_dtype, v_cache_dtype = self.vllm_config.quant_config.get_kv_quant_dtype(
layer_name, kv_cache_spec.dtype, self.model_config
layer_name, current_kv_cache_spec.dtype, self.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)

if self.use_sparse:
dsa_k_cache_shape = (
num_blocks,
kv_cache_spec.block_size,
kv_cache_spec.num_kv_heads,
current_kv_cache_spec.block_size,
current_kv_cache_spec.num_kv_heads,
self.model_config.hf_text_config.index_head_dim,
)
if self.use_sparse_c8_indexer:
Expand All @@ -2905,8 +2934,8 @@ def _reshape_kv_cache_tensors(
# dsa_k_scale
dsa_k_scale_cache_shape = (
num_blocks,
kv_cache_spec.block_size,
kv_cache_spec.num_kv_heads,
current_kv_cache_spec.block_size,
current_kv_cache_spec.num_kv_heads,
1,
)
assert raw_dsa_k_scale_tensor is not None
Expand All @@ -2918,15 +2947,15 @@ def _reshape_kv_cache_tensors(
kv_caches[layer_name] = (k_cache, v_cache, dsa_k_cache, dsa_k_scale_cache)
else:
# dsa_k
dsa_k_cache = raw_dsa_k_tensor.view(kv_cache_spec.dtype).view(dsa_k_cache_shape)
dsa_k_cache = raw_dsa_k_tensor.view(current_kv_cache_spec.dtype).view(dsa_k_cache_shape)
kv_caches[layer_name] = (k_cache, v_cache, dsa_k_cache)
else:
kv_caches[layer_name] = (k_cache, v_cache)
elif isinstance(kv_cache_spec, MambaSpec):
elif isinstance(current_kv_cache_spec, MambaSpec):
raw_tensor = kv_cache_raw_tensors[layer_name]
assert raw_tensor is not None
assert raw_tensor.numel() % kv_cache_spec.page_size_bytes == 0
num_blocks = raw_tensor.numel() // kv_cache_spec.page_size_bytes
assert raw_tensor.numel() % current_kv_cache_spec.page_size_bytes == 0
num_blocks = raw_tensor.numel() // current_kv_cache_spec.page_size_bytes
assert num_blocks >= kv_cache_config.num_blocks

# `num_blocks` is the number of blocks the model runner can use.
Expand All @@ -2946,7 +2975,7 @@ def _reshape_kv_cache_tensors(
# tensor1: [(kv_padding), conv , ...]
# tensor2: [k , ssm , ...]
# tensor3: [v , (mamba_padding), ...]
for shape, dtype in zip(kv_cache_spec.shapes, kv_cache_spec.dtypes):
for shape, dtype in zip(current_kv_cache_spec.shapes, current_kv_cache_spec.dtypes):
# normally, there is conv state and ssm state in this loop. And there is only
# a conv state in some special models.
target_shape = (num_blocks, *shape)
Expand Down
Loading