Compare commits

..
Author SHA1 Message Date
yewentao256 9e766ef514 remove multiple dead codes
Signed-off-by: yewentao256 <zhyanwentao@126.com>
2026-07-29 15:12:48 +00:00
37 changed files with 31 additions and 775 deletions
@@ -942,10 +942,9 @@ static void launchFullCacheKernel(
// ────────────────────────────────────────────────────────────────────────────
// Torch op wrapper
// ────────────────────────────────────────────────────────────────────────────
void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
torch::stable::Tensor fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert(
torch::stable::Tensor const& q_in, // [N, num_heads_q, 512] bf16
torch::stable::Tensor const& kv, // [N, 512] bf16 (read-only)
torch::stable::Tensor& q_out, // [N, q_head_padded, 512]
torch::stable::Tensor& k_cache, // [num_blocks, block_bytes] uint8
torch::stable::Tensor const& slot_mapping, // [N] int64
torch::stable::Tensor const& position_ids, // [N] int64
@@ -971,16 +970,8 @@ void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
STD_TORCH_CHECK(kv.dim() == 2 && kv.size(1) == 512, "kv shape [N, 512]");
STD_TORCH_CHECK(q_in.scalar_type() == kv.scalar_type(),
"q_in and kv dtype must match");
STD_TORCH_CHECK(q_out.device() == q_in.device() && q_out.is_contiguous(),
"q_out must be contiguous and on the same device as q_in");
STD_TORCH_CHECK(q_out.scalar_type() == q_in.scalar_type(),
"q_out dtype must match q_in");
STD_TORCH_CHECK(q_head_padded >= q_in.size(1),
"q_head_padded must be >= q_in.size(1) (num_heads_q)");
STD_TORCH_CHECK(q_out.dim() == 3 && q_out.size(0) == q_in.size(0) &&
q_out.size(1) == q_head_padded &&
q_out.size(2) == q_in.size(2),
"q_out shape [N, q_head_padded, 512]");
STD_TORCH_CHECK(k_cache.scalar_type() == torch::headeronly::ScalarType::Byte,
"k_cache must be uint8");
STD_TORCH_CHECK(cos_sin_cache.dim() == 2 && cos_sin_cache.size(1) == 64,
@@ -1008,6 +999,11 @@ void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
q_in.get_device_index());
const cudaStream_t stream = get_current_cuda_stream(q_in.get_device_index());
// Allocate the padded q output. The kernel writes every element (live
// region gets RMSNorm+RoPE; pad region gets zeros), so `empty` is safe.
auto q_out = torch::stable::new_empty(
q_in, {q_in.size(0), q_head_padded, q_in.size(2)}, q_in.scalar_type());
VLLM_STABLE_DISPATCH_HALF_TYPES(
q_in.scalar_type(), "fused_deepseek_v4_qnorm_rope_kv_insert", [&] {
using qkv_scalar_t = scalar_t;
@@ -1024,20 +1020,6 @@ void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
num_heads_q_padded, cache_block_size_i, kv_block_stride,
stream);
});
}
torch::stable::Tensor fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert(
torch::stable::Tensor const& q_in, torch::stable::Tensor const& kv,
torch::stable::Tensor& k_cache,
torch::stable::Tensor const& slot_mapping,
torch::stable::Tensor const& position_ids,
torch::stable::Tensor const& cos_sin_cache, int64_t q_head_padded,
double eps, int64_t cache_block_size) {
auto q_out = torch::stable::new_empty(
q_in, {q_in.size(0), q_head_padded, q_in.size(2)}, q_in.scalar_type());
fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
q_in, kv, q_out, k_cache, slot_mapping, position_ids, cos_sin_cache,
q_head_padded, eps, cache_block_size);
return q_out;
}
-8
View File
@@ -269,14 +269,6 @@ torch::stable::Tensor fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert(
torch::stable::Tensor const& cos_sin_cache, int64_t q_head_padded,
double eps, int64_t cache_block_size);
void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
torch::stable::Tensor const& q_in, torch::stable::Tensor const& kv,
torch::stable::Tensor& q_out, torch::stable::Tensor& k_cache,
torch::stable::Tensor const& slot_mapping,
torch::stable::Tensor const& position_ids,
torch::stable::Tensor const& cos_sin_cache, int64_t q_head_padded,
double eps, int64_t cache_block_size);
void fused_deepseek_v4_qnorm_rope_kv_rope_full_cache_bf16_insert(
torch::stable::Tensor& q, torch::stable::Tensor const& kv,
torch::stable::Tensor& k_cache, torch::stable::Tensor const& slot_mapping,
-7
View File
@@ -433,11 +433,6 @@ STABLE_TORCH_LIBRARY_FRAGMENT(_C, ops) {
"Tensor q_in, Tensor kv, Tensor! k_cache, "
"Tensor slot_mapping, Tensor position_ids, Tensor cos_sin_cache, "
"int q_head_padded, float eps, int cache_block_size) -> Tensor");
ops.def(
"fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out("
"Tensor q_in, Tensor kv, Tensor! q_out, Tensor! k_cache, "
"Tensor slot_mapping, Tensor position_ids, Tensor cos_sin_cache, "
"int q_head_padded, float eps, int cache_block_size) -> ()");
// FlashInfer V4 full-cache variants: write Q in place (bf16) or to a separate
// FP8 tensor, and KV into a contiguous 512-wide token-strided cache.
@@ -756,8 +751,6 @@ STABLE_TORCH_LIBRARY_IMPL(_C, CUDA, ops) {
ops.impl("fused_qk_norm_rope", TORCH_BOX(&fused_qk_norm_rope));
ops.impl("fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert",
TORCH_BOX(&fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert));
ops.impl("fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out",
TORCH_BOX(&fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out));
ops.impl(
"fused_deepseek_v4_qnorm_rope_kv_rope_full_cache_bf16_insert",
TORCH_BOX(&fused_deepseek_v4_qnorm_rope_kv_rope_full_cache_bf16_insert));
-18
View File
@@ -20,7 +20,6 @@ import torch
from vllm import _custom_ops as ops
from vllm.models.deepseek_v4.common.ops import (
compute_global_topk_indices_and_lens,
dequantize_and_gather_k_cache,
quantize_and_insert_k_cache,
)
@@ -35,23 +34,6 @@ from vllm.platforms import current_platform
from .test_fused_indexer_q_rope_quant import quantize_to_mxfp4
def test_compute_global_topk_reuses_output_buffers():
device = "cuda"
topk_indices = torch.tensor(
[[0, 3, -1], [1, 2, -1]], dtype=torch.int32, device=device
)
token_to_req = torch.tensor([0, 1], dtype=torch.int32, device=device)
block_table = torch.tensor([[5, 7], [11, 13]], dtype=torch.int32, device=device)
is_valid = torch.tensor([True, False], device=device)
args = (topk_indices, token_to_req, block_table, 2, is_valid)
expected = compute_global_topk_indices_and_lens(*args)
outputs = tuple(torch.empty_like(tensor) for tensor in expected)
actual = compute_global_topk_indices_and_lens(*args, output_buffers=outputs)
for result, output, reference in zip(actual, outputs, expected):
assert result.data_ptr() == output.data_ptr()
torch.testing.assert_close(result, reference)
def _ue8m0_reference(x: torch.Tensor, block_size: int, fp8_max: float):
"""PyTorch reference for UE8M0 FP8 quantization (per-block, power-of-2 scale).
@@ -257,18 +257,8 @@ def test_q_path_matches_reference(num_tokens: int, n_heads: int, padded_heads: i
num_blocks, bs, HEAD_BYTES, dtype=torch.uint8, device=device
).view(num_blocks, -1)
slot_mapping = torch.full((num_tokens,), -1, dtype=torch.int64, device=device)
q_out = torch.empty(num_tokens, padded_heads, HEAD_DIM, dtype=dtype, device=device)
torch.ops._C.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
q,
kv,
q_out,
k_cache,
slot_mapping,
positions,
cos_sin_cache,
padded_heads,
eps,
bs,
q_out = _call_fused(
q, padded_heads, kv, k_cache, slot_mapping, positions, cos_sin_cache, eps, bs
)
torch.testing.assert_close(q_out[:, :n_heads], q_ref, rtol=1e-2, atol=1e-2)
@@ -150,23 +150,6 @@ def test_fused_indexer_q_rope_quant_matches_unfused(
q_quant_ref, weights_ref = _reference(
positions, q, cos_sin_cache, weights, softmax_scale, head_scale, use_fp4
)
output_buffers: tuple[torch.Tensor, ...] | None = None
OUTPUT_BUFFER_TEST_NUM_TOKENS = 7
if num_tokens == OUTPUT_BUFFER_TEST_NUM_TOKENS and cache_dtype == torch.float32:
if use_fp4:
q_ref, q_scale_ref = q_quant_ref
output_buffers = (
torch.empty_like(q_ref),
torch.empty_like(q_scale_ref)
.view(torch.uint8)
.reshape(num_tokens, N_HEAD, -1),
torch.empty_like(weights_ref),
)
else:
output_buffers = (
torch.empty_like(q_quant_ref),
torch.empty_like(weights_ref),
)
# use_cutedsl=False: force the triton path even when cutedsl is installed
# by patching the dispatcher's has_cutedsl() binding to return False.
cutedsl_patch = (
@@ -186,17 +169,8 @@ def test_fused_indexer_q_rope_quant_matches_unfused(
softmax_scale,
head_scale,
use_fp4,
output_buffers=output_buffers,
)
if output_buffers is not None:
if use_fp4:
assert q_quant_fused[0].data_ptr() == output_buffers[0].data_ptr()
assert q_quant_fused[1].data_ptr() == output_buffers[1].data_ptr()
else:
assert q_quant_fused.data_ptr() == output_buffers[0].data_ptr()
assert weights_fused.data_ptr() == output_buffers[-1].data_ptr()
if use_fp4:
q_quant_ref, q_scale_ref = q_quant_ref
q_quant_fused, q_scale_fused = q_quant_fused
-19
View File
@@ -14,10 +14,6 @@ def is_func(node: fx.Node, target: Target) -> bool:
return bool(node.op == "call_function" and node.target == target)
def is_auto_func(node: fx.Node, op: OpOverload) -> bool:
return is_func(node, auto_functionalized) and node.args[0] == op
# Returns the first auto_functionalized node with the given op (if it exists)
def find_auto_fn_maybe(nodes: Iterable[fx.Node], op: OpOverload) -> fx.Node | None:
for node in nodes:
@@ -42,13 +38,6 @@ def find_getitem_maybe(node: fx.Node, idx: int) -> fx.Node | None:
return None
# Returns the getitem node that extracts the idx-th element from node
def find_getitem(node: fx.Node, idx: int) -> fx.Node:
ret = find_getitem_maybe(node, idx)
assert ret is not None, f"Could not find getitem {idx} in node {node}"
return ret
# An auto-functionalization-aware utility for finding nodes with a specific op
# Also handles op overload packets and finds all overloads
def find_op_nodes(
@@ -67,11 +56,3 @@ def find_op_nodes(
for n in graph.find_nodes(op="call_function", target=auto_functionalized):
if n.args[0] == op:
yield n
# Asserts that the node only has one user and returns it
# Even if a node has only 1 user, it might share storage with another node,
# which might need to be taken into account.
def get_only_user(node: fx.Node) -> fx.Node:
assert len(node.users) == 1
return next(iter(node.users))
@@ -333,12 +333,3 @@ class VllmFusionPatternMatcherPass(VllmPatternMatcherPass):
def __call__(self, graph: torch.fx.Graph) -> None:
self.matched_count = self.pm_pass.apply(graph)
VllmPatternMatcherPass.match_table[self.pass_name] += self.matched_count
class PrinterInductorPass(VllmInductorPass):
def __init__(self, name: str, config: VllmConfig) -> None:
super().__init__(config)
self.name = name
def __call__(self, graph: torch.fx.Graph) -> None:
self.dump_graph(graph, self.name)
-25
View File
@@ -415,31 +415,6 @@ class Range:
return self.__str__()
def handle_deprecated(
config: ConfigT,
old_name: str,
new_name_or_names: str | list[str],
removal_version: str,
) -> None:
old_val = getattr(config, old_name)
if old_val is None:
return
if isinstance(new_name_or_names, str):
new_names = [new_name_or_names]
else:
new_names = new_name_or_names
msg = (
f"{old_name} is deprecated and will be removed in {removal_version}. "
f"Use {', '.join(new_names)} instead."
)
logger.warning(msg)
for new_name in new_names:
setattr(config, new_name, old_val)
def get_from_deprecated_env_if_set(
env_name: str,
removal_version: str,
-3
View File
@@ -2081,9 +2081,6 @@ def model_parallel_is_initialized():
return _TP is not None and _PP is not None
_TP_STATE_PATCHED = False
def get_tensor_model_parallel_world_size() -> int:
"""Return world size for the tensor model parallel group."""
return get_tp_group().world_size
-9
View File
@@ -2015,15 +2015,6 @@ async def parse_chat_messages_async(
return conversation, mm_data, mm_uuids
def get_history_tool_calls_cnt(conversation: list[ConversationMessage]):
idx = 0
for msg in conversation:
if msg["role"] == "assistant":
tool_calls = msg.get("tool_calls")
idx += len(list(tool_calls)) if tool_calls is not None else 0 # noqa
return idx
_KIMI_MODEL_TYPES = ("kimi_k2", "kimi_k25", "kimi_k3")
-48
View File
@@ -8,14 +8,8 @@ import torch
import torch.nn as nn
import torch.nn.functional as F
from vllm.distributed import (
divide,
get_tensor_model_parallel_rank,
get_tensor_model_parallel_world_size,
)
from vllm.logger import init_logger
from vllm.model_executor.custom_op import CustomOp
from vllm.model_executor.utils import set_weight_attrs
from vllm.platforms import CpuArchEnum, current_platform
from vllm.triton_utils import tl, triton
from vllm.utils.collection_utils import LazyDict
@@ -788,48 +782,6 @@ class XIELU(CustomOp):
return self.forward_native(input)
class ScaledActivation(nn.Module):
"""An activation function with post-scale parameters.
This is used for some quantization methods like AWQ.
"""
def __init__(
self,
act_module: nn.Module,
intermediate_size: int,
input_is_parallel: bool = True,
params_dtype: torch.dtype | None = None,
):
super().__init__()
self.act = act_module
self.input_is_parallel = input_is_parallel
if input_is_parallel:
tp_size = get_tensor_model_parallel_world_size()
intermediate_size_per_partition = divide(intermediate_size, tp_size)
else:
intermediate_size_per_partition = intermediate_size
if params_dtype is None:
params_dtype = torch.get_default_dtype()
self.scales = nn.Parameter(
torch.empty(intermediate_size_per_partition, dtype=params_dtype)
)
set_weight_attrs(self.scales, {"weight_loader": self.weight_loader})
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.act(x) / self.scales
def weight_loader(self, param: nn.Parameter, loaded_weight: torch.Tensor):
param_data = param.data
if self.input_is_parallel:
tp_rank = get_tensor_model_parallel_rank()
shard_size = param_data.shape[0]
start_idx = tp_rank * shard_size
loaded_weight = loaded_weight.narrow(0, start_idx, shard_size)
assert param_data.shape == loaded_weight.shape
param_data.copy_(loaded_weight)
_ACTIVATION_REGISTRY = LazyDict(
{
"gelu": lambda: GELU(),
-52
View File
@@ -155,58 +155,6 @@ class Conv2dLayer(ConvLayerBase):
return self._forward_conv(x)
class CausalConv2dLayer(Conv2dLayer):
"""
A causal version of nn.Conv2d where each location in the 2D matrix would
have no access to locations on its right or down
All arguments are the same as nn.Conv2d except padding which should be
set as None
"""
def __init__(
self,
in_channels: int,
out_channels: int,
kernel_size: int,
stride: int,
padding: int = 0,
dilation: int = 1,
groups: int = 1,
bias: bool = True,
padding_mode: str = "zeros",
*,
params_dtype: torch.dtype | None = None,
) -> None:
if padding is not None:
raise ValueError(
"Argument padding should be set to None for CausalConv2dLayer."
)
self._left_padding: int = kernel_size - 1
self._right_padding: int = stride - 1
padding = 0
super().__init__(
in_channels,
out_channels,
kernel_size,
stride,
padding,
dilation,
groups,
bias,
padding_mode,
params_dtype=params_dtype,
)
def forward(
self,
x: torch.Tensor,
) -> torch.Tensor:
x = F.pad(x, pad=(self._left_padding, self._right_padding, 0, 0))
x = super().forward(x)
return x
# --8<-- [start:conv3d]
@CustomOp.register("conv3d")
class Conv3dLayer(ConvLayerBase):
@@ -4,7 +4,6 @@
from typing import TYPE_CHECKING, Any
import torch
from torch.utils._python_dispatch import TorchDispatchMode
import vllm.envs as envs
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
@@ -234,26 +233,6 @@ class Fp8Config(QuantizationConfig):
return cache_scale_mapper | QuantizationConfig.get_cache_scale_mapper()
class CopyNumelCounter(TorchDispatchMode):
"""
Tracks total number of elements modified with `copy_`. Useful for keeping
track of weight loading where underlying weights can be arbitrarily
transformed (such as with `narrow`) before calling copy.
"""
def __init__(self):
super().__init__()
self.copied_numel = 0
def __torch_dispatch__(self, func, types, args=(), kwargs=None):
if kwargs is None:
kwargs = {}
out = func(*args, **kwargs)
if func == torch.ops.aten.copy_.default:
self.copied_numel += args[0].numel()
return out
class Fp8LinearMethod(LinearMethodBase):
"""Linear method for FP8.
Supports loading FP8 checkpoints with static weight scale and
-6
View File
@@ -25,12 +25,6 @@ MOE_LAYER_ROUTER_GATE_SUFFIXES = {
}
def is_layer_moe_router_gate(prefix: str) -> bool:
if not prefix:
return False
return prefix.rsplit(".", 1)[-1] in MOE_LAYER_ROUTER_GATE_SUFFIXES
def get_token_bin_counts_and_mask(
tokens: torch.Tensor,
vocab_size: int,
-4
View File
@@ -242,10 +242,6 @@ class GlmOcrVisionBlock(Glm4vVisionBlock):
)
class GlmOcrVisionPatchEmbed(Glm4vVisionPatchEmbed):
pass
class GlmOcrPatchMerger(Glm4vPatchMerger):
pass
-35
View File
@@ -103,41 +103,6 @@ class Idefics3ProcessingInfo(BaseProcessingInfo):
def get_supported_mm_limits(self) -> Mapping[str, int | None]:
return {"image": None}
def _resize_output_size(
self,
*,
height: int,
width: int,
max_len: int | None = None,
min_len: int = 1,
max_size: int | None = None,
) -> tuple[int, int]:
# Set default value for max_len if not provided
max_len = max(height, width) if max_len is None else max_len
aspect_ratio = width / height
# Handle the maximum size constraint
if max_size is not None:
max_len = min(max_len, max_size)
# Adjust dimensions according to the aspect ratio
if width >= height:
width = max_len
height = int(width / aspect_ratio)
else:
height = max_len
width = int(height * aspect_ratio)
# Ensure both width and height are even (if needed)
height += height % 2
width += width % 2
# Ensure dimensions are not smaller than the minimum length
height = max(height, min_len)
width = max(width, min_len)
return height, width
def _get_image_feature_grid_size(
self,
*,
@@ -1531,13 +1531,6 @@ class LlavaOnevision2MultiModalDataParser(MultiModalDataParser):
class LlavaOnevision2MultiModalProcessor(
BaseMultiModalProcessor[LlavaOnevision2ProcessingInfo]
):
def _get_data_parser(self) -> MultiModalDataParser:
# Retained for symmetry; vLLM actually fetches the parser via
# info.get_data_parser() (see ProcessingInfo override above).
return LlavaOnevision2MultiModalDataParser(
self.info.get_hf_config().vision_config.spatial_merge_size
)
def _call_hf_processor(
self,
prompt: str,
@@ -77,24 +77,6 @@ class BartScaledWordEmbedding(VocabParallelEmbedding):
return super().forward(input_ids) * self.embed_scale
class BartParallelLMHead(ParallelLMHead):
"""
This module overrides ParallelLMHead's
forward by dividing by embeddings scale,
yielding effectively the inverse of
BartScaledWordEmbedding
"""
def __init__(
self, num_embeddings: int, embedding_dim: int, embed_scale: float = 1.0
):
super().__init__(num_embeddings, embedding_dim)
self.embed_scale = embed_scale
def forward(self, input_ids: torch.Tensor) -> torch.Tensor:
return super().forward(input_ids) / self.embed_scale
class BartDecoderLayer(nn.Module):
def __init__(
self,
-26
View File
@@ -521,32 +521,6 @@ class Phi4MMAudioEmbeddingInputs(TensorSchema):
Phi4MMAudioInputs: TypeAlias = Phi4MMAudioFeatureInputs | Phi4MMAudioEmbeddingInputs
def cat_with_pad(tensors, dim, padding_value=0):
"""
cat along dim, while pad to max for all other dims
"""
ndim = tensors[0].dim()
assert all(t.dim() == ndim for t in tensors[1:]), (
"All tensors must have the same number of dimensions"
)
out_size = [max(t.shape[i] for t in tensors) for i in range(ndim)]
out_size[dim] = sum(t.shape[dim] for t in tensors)
output = tensors[0].new_full(out_size, padding_value)
index = 0
for t in tensors:
# Create a slice list where every dimension except dim is full slice
slices = [slice(0, t.shape[d]) for d in range(ndim)]
# Update only the concat dimension slice
slices[dim] = slice(index, index + t.shape[dim])
output[slices] = t
index += t.shape[dim]
return output
def stack_with_pad(
tensors: torch.Tensor | list[torch.Tensor],
padding_value: int | float = 0,
@@ -1595,14 +1595,6 @@ class AttModule(nn.Module):
return x, memory, pos_emb, att_mask
class AttBlock(BlockBase, AttModule):
"""Attention Block module to support both Attention and Block module."""
def memory_dims(self, max_len: bool = False) -> tuple[int, int]:
"""memory dimensions"""
return (1, self.input_size)
def masked_softmax(
scores: Tensor,
mask: Tensor | None,
+1 -31
View File
@@ -7,7 +7,7 @@
# Copyright (c) 2025 Skywork
# Licensed under The MIT License [see LICENSE for details]
# --------------------------------------------------------
from collections.abc import Iterable, Mapping
from collections.abc import Iterable
from typing import Annotated, Literal, TypeAlias
import torch
@@ -15,8 +15,6 @@ import torch.nn as nn
from transformers import PretrainedConfig
from vllm.config import VllmConfig
from vllm.config.multimodal import BaseDummyOptions
from vllm.inputs import MultiModalDataDict
from vllm.model_executor.layers.linear import ReplicatedLinear
from vllm.model_executor.layers.quantization import QuantizationConfig
from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig
@@ -24,7 +22,6 @@ from vllm.model_executor.models.intern_vit import (
InternVisionModel,
)
from vllm.multimodal import MULTIMODAL_REGISTRY
from vllm.multimodal.processing import BaseDummyInputsBuilder
from vllm.sequence import IntermediateTensors
from vllm.transformers_utils.processors.internvl import (
InternVLImageProcessor,
@@ -117,33 +114,6 @@ class SkyworkR1VProcessingInfo(BaseInternVLProcessingInfo):
)
class SkyworkR1VDummyInputsBuilder(BaseDummyInputsBuilder[SkyworkR1VProcessingInfo]):
def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str:
num_images = mm_counts.get("image", 0)
return "<image>" * num_images
def get_dummy_mm_data(
self,
seq_len: int,
mm_counts: Mapping[str, int],
mm_options: Mapping[str, BaseDummyOptions],
) -> MultiModalDataDict:
target_width, target_height = self.info.get_image_size_with_most_features()
num_images = mm_counts.get("image", 0)
image_overrides = mm_options.get("image")
return {
"image": self._get_dummy_images(
width=target_width,
height=target_height,
num_images=num_images,
overrides=image_overrides,
)
}
@MULTIMODAL_REGISTRY.register_processor(
BaseInternVLMultiModalProcessor,
info=SkyworkR1VProcessingInfo,
-12
View File
@@ -27,7 +27,6 @@ from vllm.multimodal import NestedTensors
from vllm.sequence import IntermediateTensors
from vllm.utils.math_utils import cdiv
from vllm.utils.torch_utils import (
async_tensor_h2d,
direct_register_custom_op,
)
@@ -673,17 +672,6 @@ def _merge_multimodal_embeddings(
return inputs_embeds
def isin_list(
elements: torch.Tensor,
test_elements_list: list[int],
) -> torch.Tensor:
test_elements = async_tensor_h2d(
test_elements_list, dtype=torch.int64, device=elements.device
)
return torch.isin(elements, test_elements)
class StageMissingLayer(nn.Module):
def __init__(self, stage_name: str, module: nn.Module | None = None) -> None:
super().__init__()
-37
View File
@@ -580,40 +580,3 @@ def run_dp_sharded_mrope_vision_model(
"Found unassigned embeddings"
)
return out_embeddings
def get_llm_pos_ids_for_vision(
start_idx: int,
vision_idx: int,
spatial_merge_size: int,
t_index: list[int],
grid_hs: torch.Tensor,
grid_ws: torch.Tensor,
) -> torch.Tensor:
llm_pos_ids_list = []
llm_grid_h = grid_hs[vision_idx] // spatial_merge_size
llm_grid_w = grid_ws[vision_idx] // spatial_merge_size
h_index = (
torch.arange(llm_grid_h)
.view(1, -1, 1)
.expand(len(t_index), -1, llm_grid_w)
.flatten()
)
w_index = (
torch.arange(llm_grid_w)
.view(1, 1, -1)
.expand(len(t_index), llm_grid_h, -1)
.flatten()
)
t_index_tensor = (
torch.Tensor(t_index)
.to(llm_grid_h.device)
.view(-1, 1)
.expand(-1, llm_grid_h * llm_grid_w)
.long()
.flatten()
)
_llm_pos_ids = torch.stack([t_index_tensor, h_index, w_index])
llm_pos_ids_list.append(_llm_pos_ids + start_idx)
llm_pos_ids = torch.cat(llm_pos_ids_list, dim=1)
return llm_pos_ids
+2 -35
View File
@@ -29,7 +29,6 @@ from vllm.models.deepseek_v4.common.ops import (
from vllm.models.deepseek_v4.common.ops.fused_indexer_q import MXFP4_BLOCK_SIZE
if TYPE_CHECKING:
from vllm.models.deepseek_v4.eager_scratch import DeepseekV4EagerScratchPool
from vllm.v1.attention.backends.mla.sparse_swa import (
DeepseekSparseSWAMetadata,
)
@@ -182,7 +181,6 @@ class DeepseekV4Attention(nn.Module, AttentionLayerBase, ABC):
prefix: str,
topk_indices_buffer: torch.Tensor | None = None,
aux_stream_list: list[torch.cuda.Stream] | None = None,
eager_scratch_pool: "DeepseekV4EagerScratchPool | None" = None,
) -> None:
super().__init__()
config = vllm_config.model_config.hf_config
@@ -271,7 +269,6 @@ class DeepseekV4Attention(nn.Module, AttentionLayerBase, ABC):
)
self.indexer_rotary_emb = self.rotary_emb
self.topk_indices_buffer = topk_indices_buffer
self.eager_scratch_pool = eager_scratch_pool
self.indexer = None
if self.compress_ratio == 4:
@@ -293,7 +290,6 @@ class DeepseekV4Attention(nn.Module, AttentionLayerBase, ABC):
compress_ratio=self.compress_ratio,
prefix=f"{prefix}.indexer",
aux_stream=indexer_aux_stream,
eager_scratch_pool=eager_scratch_pool,
)
# Will be None on ROCm for now.
@@ -344,7 +340,6 @@ class DeepseekV4Attention(nn.Module, AttentionLayerBase, ABC):
rotate=True,
prefix=f"{prefix}.compressor",
k_cache_prefix=self.prefix,
eager_scratch_pool=eager_scratch_pool,
)
def forward(
@@ -573,24 +568,10 @@ class DeepseekV4Attention(nn.Module, AttentionLayerBase, ABC):
if cache_dtype == torch.uint8:
# fp8_ds_mla UE8M0 paged path. Horizontally fused:
# Q side: per-head RMSNorm (no weight) + GPT-J RoPE, zero-filling
# the padding head slots.
# the padding head slots; the kernel allocates and returns
# the padded q tensor.
# KV side: GPT-J RoPE + UE8M0 FP8 quant + paged cache insert.
swa_kv_cache_2d = swa_kv_cache.view(swa_kv_cache.shape[0], -1)
if self.eager_scratch_pool is not None:
q_out = self.eager_scratch_pool.q_out(q.shape[0])
torch.ops._C.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
q,
kv,
q_out,
swa_kv_cache_2d,
swa_metadata.slot_mapping,
positions,
cos_sin_cache,
self.padded_heads,
self.eps,
swa_metadata.block_size,
)
return q_out
return torch.ops._C.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert(
q,
kv,
@@ -639,13 +620,6 @@ class DeepseekV4Attention(nn.Module, AttentionLayerBase, ABC):
)
return q_fp8
def _global_topk_output_buffers(
self, topk_indices: torch.Tensor
) -> tuple[torch.Tensor, torch.Tensor] | None:
if self.compress_ratio != 4 or self.eager_scratch_pool is None:
return None
return self.eager_scratch_pool.global_topk_outputs(topk_indices)
def get_attn_backend(self) -> type[AttentionBackend]:
return self.backend_cls
@@ -725,7 +699,6 @@ class DeepseekV4Indexer(nn.Module):
compress_ratio: int = 1,
prefix: str = "",
aux_stream: torch.cuda.Stream | None = None,
eager_scratch_pool: "DeepseekV4EagerScratchPool | None" = None,
):
super().__init__()
self.vllm_config = vllm_config
@@ -738,7 +711,6 @@ class DeepseekV4Indexer(nn.Module):
self.rope_dim = config.qk_rope_head_dim # 64
self.q_lora_rank = q_lora_rank # 1536
self.compress_ratio = compress_ratio
self.eager_scratch_pool = eager_scratch_pool
self.use_fp4_kv = self.vllm_config.attention_config.use_fp4_indexer_cache
logger.info_once(
"Using %s indexer cache for Lightning Indexer.",
@@ -802,7 +774,6 @@ class DeepseekV4Indexer(nn.Module):
prefix=f"{prefix}.compressor",
k_cache_prefix=self.k_cache.prefix,
use_fp4_cache=self.use_fp4_kv,
eager_scratch_pool=eager_scratch_pool,
)
self.indexer_op = SparseAttnIndexer(
@@ -863,9 +834,6 @@ class DeepseekV4Indexer(nn.Module):
# ReplicatedLinear returns (output, bias); bias is None.
q, _ = self.wq_b(qr)
q = q.view(-1, self.n_head, self.head_dim)
outputs = None
if self.eager_scratch_pool is not None and self.use_fp4_kv:
outputs = self.eager_scratch_pool.indexer_q_outputs(q.shape[0])
return fused_indexer_q_rope_quant(
positions,
q,
@@ -874,7 +842,6 @@ class DeepseekV4Indexer(nn.Module):
self.softmax_scale,
self.n_head**-0.5,
use_fp4=self.use_fp4_kv,
output_buffers=outputs,
)
# compressor returns None and writes K to the indexer KV cache; the
@@ -438,7 +438,6 @@ def compute_global_topk_indices_and_lens(
block_table: torch.Tensor,
block_size: int,
is_valid_token: torch.Tensor,
output_buffers: tuple[torch.Tensor, torch.Tensor] | None = None,
) -> tuple[torch.Tensor, torch.Tensor]:
"""Map local topk indices to global KV cache slots and count valid entries.
@@ -448,15 +447,8 @@ def compute_global_topk_indices_and_lens(
3. Masking padding tokens to length 0
"""
num_tokens = topk_indices.shape[0]
if output_buffers is None:
global_topk_indices = torch.empty_like(topk_indices)
topk_lens = torch.empty(
num_tokens, dtype=torch.int32, device=topk_indices.device
)
else:
global_topk_indices, topk_lens = output_buffers
assert global_topk_indices.shape == topk_indices.shape
assert topk_lens.shape == (num_tokens,)
global_topk_indices = torch.empty_like(topk_indices)
topk_lens = torch.empty(num_tokens, dtype=torch.int32, device=topk_indices.device)
_compute_global_topk_indices_and_lens_kernel[(num_tokens,)](
global_topk_indices,
global_topk_indices.stride(0),
@@ -295,7 +295,6 @@ def fused_indexer_q_rope_quant(
index_weights_softmax_scale: float,
index_weights_head_scale: float,
use_fp4: bool = False,
output_buffers: tuple[torch.Tensor, ...] | None = None,
) -> tuple[
torch.Tensor | tuple[torch.Tensor, torch.Tensor],
torch.Tensor,
@@ -333,13 +332,7 @@ def fused_indexer_q_rope_quant(
num_index_q_heads = index_q.shape[1]
index_q_head_dim = index_q.shape[2]
if output_buffers is None:
index_weights_out = torch.empty_like(index_weights, dtype=torch.float32)
else:
expected_num_buffers = 3 if use_fp4 else 2
assert len(output_buffers) == expected_num_buffers
index_weights_out = output_buffers[-1]
assert index_weights_out.shape == index_weights.shape
index_weights_out = torch.empty_like(index_weights, dtype=torch.float32)
if use_fp4:
assert index_q_head_dim % MXFP4_BLOCK_SIZE == 0, (
@@ -347,23 +340,16 @@ def fused_indexer_q_rope_quant(
f"size {MXFP4_BLOCK_SIZE}"
)
num_scale_blocks = index_q_head_dim // MXFP4_BLOCK_SIZE
packed_shape = (num_tokens, num_index_q_heads, index_q_head_dim // 2)
scale_shape = (num_tokens, num_index_q_heads, num_scale_blocks)
if output_buffers is None:
index_q_packed = torch.empty(
packed_shape,
dtype=torch.uint8,
device=index_q.device,
)
index_q_scale = torch.empty(
scale_shape,
dtype=torch.uint8,
device=index_q.device,
)
else:
index_q_packed, index_q_scale, _ = output_buffers
assert index_q_packed.shape == packed_shape
assert index_q_scale.shape == scale_shape
index_q_packed = torch.empty(
(num_tokens, num_index_q_heads, index_q_head_dim // 2),
dtype=torch.uint8,
device=index_q.device,
)
index_q_scale = torch.empty(
(num_tokens, num_index_q_heads, num_scale_blocks),
dtype=torch.uint8,
device=index_q.device,
)
if has_cutedsl():
# lazily import, otherwise some tests fail due to CUDA driver init failure.
from vllm.models.deepseek_v4.nvidia.ops.fused_indexer_q_cutedsl import (
@@ -432,11 +418,7 @@ def fused_indexer_q_rope_quant(
fp8_dtype = current_platform.fp8_dtype()
use_fnuz = fp8_dtype == torch.float8_e4m3fnuz
fp8_max = 224.0 if use_fnuz else 448.0
if output_buffers is None:
index_q_fp8 = torch.empty_like(index_q, dtype=fp8_dtype)
else:
index_q_fp8, _ = output_buffers
assert index_q_fp8.shape == index_q.shape
index_q_fp8 = torch.empty_like(index_q, dtype=fp8_dtype)
if has_cutedsl():
# lazily import, otherwise some tests fail due to CUDA driver init failure.
from vllm.models.deepseek_v4.nvidia.ops.fused_indexer_q_cutedsl import (
+1 -10
View File
@@ -2,7 +2,7 @@
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, ClassVar, cast
from typing import Any, ClassVar, cast
import torch
from torch import nn
@@ -35,9 +35,6 @@ from vllm.v1.kv_cache_interface import (
SlidingWindowMLASpec,
)
if TYPE_CHECKING:
from vllm.models.deepseek_v4.eager_scratch import DeepseekV4EagerScratchPool
def _prefer_two_stage_compressor() -> bool:
# Platforms that favor the triton variant of two-stage compressor split.
@@ -229,7 +226,6 @@ class DeepseekCompressor(nn.Module):
prefix: str = "",
k_cache_prefix="",
use_fp4_cache: bool = False,
eager_scratch_pool: "DeepseekV4EagerScratchPool | None" = None,
):
super().__init__()
self.compress_ratio = compress_ratio
@@ -239,7 +235,6 @@ class DeepseekCompressor(nn.Module):
self.prefix = prefix
self.k_cache_prefix = k_cache_prefix
self.use_fp4_cache = use_fp4_cache
self.eager_scratch_pool = eager_scratch_pool
config = vllm_config.model_config.hf_config
self.rope_head_dim = config.qk_rope_head_dim
@@ -433,10 +428,6 @@ class DeepseekCompressor(nn.Module):
store_full_fp8=store_full_fp8,
fp8_scale=fp8_scale,
)
if not self.overlap and self.eager_scratch_pool is not None:
extra_kwargs["compress_scratch"] = (
self.eager_scratch_pool.compressor_scratch(num_actual)
)
elif self._use_two_stage_fused_compressor:
# head=512 cr>=128 (no overlap): two-pass split compressor on the
# prefill suffix, single-pass on the decode prefix.
-137
View File
@@ -1,137 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from math import prod
import torch
from vllm.models.deepseek_v4.common.ops.fused_indexer_q import MXFP4_BLOCK_SIZE
from vllm.utils.math_utils import round_up
class DeepseekV4EagerScratchPool:
"""Model-wide outputs and scratch used inside the attention eager break."""
_ALIGNMENT = 256
def __init__(
self,
max_num_tokens: int,
padded_q_heads: int,
q_head_dim: int,
index_q_heads: int,
index_q_head_dim: int,
index_topk: int,
device: torch.device | str,
) -> None:
self.max_num_tokens = max_num_tokens
self.index_topk = index_topk
self._q = torch.empty(
(max_num_tokens, padded_q_heads, q_head_dim),
dtype=torch.bfloat16,
device=device,
)
fp4_specs = (
((max_num_tokens, index_q_heads, index_q_head_dim // 2), torch.uint8),
(
(
max_num_tokens,
index_q_heads,
index_q_head_dim // MXFP4_BLOCK_SIZE,
),
torch.uint8,
),
((max_num_tokens, index_q_heads), torch.float32),
)
global_specs = (
((max_num_tokens, index_topk), torch.int32),
((max_num_tokens,), torch.int32),
)
compressor_specs = (((max_num_tokens, q_head_dim), torch.float32),)
# FP4 indexer is C4 only, global mapping after FP4 indexer
# compressor scratch is C128 only
# so here we use max instead of sum
aux_bytes = max(
self._packed_size(specs)
for specs in (fp4_specs, global_specs, compressor_specs)
)
storage = torch.empty(aux_bytes, dtype=torch.uint8, device=device)
self._q_outputs: dict[int, torch.Tensor] = {}
fp4_values, fp4_scales, fp4_weights = self._views(storage, fp4_specs)
self._fp4_template = (fp4_values, fp4_scales, fp4_weights)
self._fp4_outputs: dict[
int, tuple[torch.Tensor, torch.Tensor, torch.Tensor]
] = {}
global_indices, global_lens = self._views(storage, global_specs)
self._global_template = (global_indices, global_lens)
self._global_outputs: dict[int, tuple[torch.Tensor, torch.Tensor]] = {}
self._compressor_template = self._views(storage, compressor_specs)[0]
self._compressor_outputs: dict[int, torch.Tensor] = {}
self._storage = storage
@classmethod
def _packed_size(
cls, specs: tuple[tuple[tuple[int, ...], torch.dtype], ...]
) -> int:
offset = 0
for shape, dtype in specs:
offset = round_up(offset, cls._ALIGNMENT) + prod(shape) * dtype.itemsize
return round_up(offset, cls._ALIGNMENT)
@classmethod
def _views(
cls,
storage: torch.Tensor,
specs: tuple[tuple[tuple[int, ...], torch.dtype], ...],
) -> list[torch.Tensor]:
offset = 0
views = []
for shape, dtype in specs:
offset = round_up(offset, cls._ALIGNMENT)
num_bytes = prod(shape) * dtype.itemsize
views.append(storage[offset : offset + num_bytes].view(dtype).view(shape))
offset += num_bytes
return views
def q_out(self, num_tokens: int) -> torch.Tensor:
output = self._q_outputs.get(num_tokens)
if output is None:
output = self._q[:num_tokens]
self._q_outputs[num_tokens] = output
return output
def compressor_scratch(self, num_tokens: int) -> torch.Tensor:
output = self._compressor_outputs.get(num_tokens)
if output is None:
output = self._compressor_template[:num_tokens]
self._compressor_outputs[num_tokens] = output
return output
def indexer_q_outputs(
self,
num_tokens: int,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
output = self._fp4_outputs.get(num_tokens)
if output is None:
values, scales, weights = self._fp4_template
output = (
values[:num_tokens],
scales[:num_tokens],
weights[:num_tokens],
)
self._fp4_outputs[num_tokens] = output
return output
def global_topk_outputs(
self, topk_indices: torch.Tensor
) -> tuple[torch.Tensor, torch.Tensor]:
num_tokens, topk = topk_indices.shape
assert topk == self.index_topk
output = self._global_outputs.get(num_tokens)
if output is None:
indices, lens = self._global_template
output = (indices[:num_tokens], lens[:num_tokens])
self._global_outputs[num_tokens] = output
return output
@@ -748,9 +748,6 @@ class DeepseekV4FlashInferSM120Attention(DeepseekV4Attention):
attn_metadata.block_table[:num_decodes],
block_size,
is_valid,
output_buffers=self._global_topk_output_buffers(
self.topk_indices_buffer[:num_decode_tokens]
),
)
)
extra_sparse_indices = global_indices.view(num_decode_tokens, 1, -1)
@@ -840,7 +837,6 @@ class DeepseekV4FlashInferSM120Attention(DeepseekV4Attention):
attn_metadata.block_table,
block_size,
swa_metadata.is_valid_token[prefill_token_slice],
output_buffers=self._global_topk_output_buffers(local_topk_indices),
)
)
@@ -170,9 +170,6 @@ class DeepseekV4FlashMLAAttention(DeepseekV4Attention):
attn_metadata.block_table[:num_decodes],
block_size,
is_valid,
output_buffers=self._global_topk_output_buffers(
self.topk_indices_buffer[:num_decode_tokens]
),
)
topk_indices = global_indices.view(num_decode_tokens, 1, -1)
else:
-20
View File
@@ -66,7 +66,6 @@ from vllm.model_executor.models.utils import (
)
from vllm.model_executor.utils import set_weight_attrs
from vllm.models.deepseek_v4.attention import DeepseekV4Attention
from vllm.models.deepseek_v4.eager_scratch import DeepseekV4EagerScratchPool
from vllm.models.deepseek_v4.nvidia.flashinfer_sparse import (
DeepseekV4FlashInferMLAAttention,
DeepseekV4FlashInferSM120Attention,
@@ -799,7 +798,6 @@ class DeepseekV4DecoderLayer(nn.Module):
prefix,
topk_indices_buffer: torch.Tensor | None = None,
aux_stream_list: list[torch.cuda.Stream] | None = None,
eager_scratch_pool: DeepseekV4EagerScratchPool | None = None,
):
super().__init__()
@@ -812,7 +810,6 @@ class DeepseekV4DecoderLayer(nn.Module):
prefix=f"{prefix}.attn",
topk_indices_buffer=topk_indices_buffer,
aux_stream_list=aux_stream_list,
eager_scratch_pool=eager_scratch_pool,
)
self.ffn = DeepseekV4MoE(vllm_config, prefix=f"{prefix}.ffn")
@@ -989,22 +986,6 @@ class DeepseekV4Model(nn.Module, EagleModelMixin):
# (compressor kv_score, indexer.weights_proj, indexer.compressor
# kv_score). fused_wqa_wkv stays on the default stream.
aux_stream_list = [torch.cuda.Stream() for _ in range(3)]
padded_heads = _select_dsv4_attn_cls(vllm_config).get_padded_num_q_heads(
config.num_attention_heads // get_tensor_model_parallel_world_size()
)
self.eager_scratch_pool: DeepseekV4EagerScratchPool | None = None
if not vllm_config.parallel_config.use_ubatching:
# TODO: support dbo if needed
# this requires the buffer to have ubatch dim
self.eager_scratch_pool = DeepseekV4EagerScratchPool(
vllm_config.scheduler_config.max_num_batched_tokens,
padded_heads,
config.head_dim,
config.index_n_heads,
config.index_head_dim,
config.index_topk,
current_platform.device_type,
)
# Reserved topk indices buffer for all Indexer layers to reuse.
self.topk_indices_buffer = torch.empty(
@@ -1030,7 +1011,6 @@ class DeepseekV4Model(nn.Module, EagleModelMixin):
prefix=prefix,
topk_indices_buffer=self.topk_indices_buffer,
aux_stream_list=aux_stream_list,
eager_scratch_pool=self.eager_scratch_pool,
),
prefix=f"{prefix}.layers",
)
@@ -2097,7 +2097,6 @@ def compress_norm_rope_store_cutedsl(
store_full_kv: bool = False,
store_full_fp8: bool = False,
fp8_scale: torch.Tensor | None = None,
compress_scratch: torch.Tensor | None = None,
) -> None:
if compress_ratio == 4:
# For C4A, the single fused kernel is faster than the two-kernel version.
@@ -2130,15 +2129,11 @@ def compress_norm_rope_store_cutedsl(
)
else:
# For C128, the two-kernel version is faster than the single fused kernel.
if compress_scratch is None:
compressed_kv = torch.empty(
(num_actual, head_dim),
dtype=torch.float32,
device=state_cache.device,
)
else:
assert compress_scratch.shape == (num_actual, head_dim)
compressed_kv = compress_scratch
compressed_kv = torch.empty(
(num_actual, head_dim),
dtype=torch.float32,
device=state_cache.device,
)
split_kv_compress_norm_rope_insert_sparse_attn_cutedsl(
state_cache,
token_to_req_indices,
-9
View File
@@ -12,15 +12,6 @@ from torch._C._profiler import _EventType, _ProfilerEvent, _TensorMetadata
#
def trim_string_front(string: str, width: int) -> str:
if len(string) > width:
offset = len(string) - width + 3
string = string[offset:]
if len(string) > 3:
string = "..." + string[3:]
return string
def trim_string_back(string: str, width: int) -> str:
if len(string) > width:
offset = len(string) - width + 3
-4
View File
@@ -182,10 +182,6 @@ class LRUCache(cachetools.LRUCache[_K, _V]):
self.popitem(remove_pinned=remove_pinned)
def _remove_old_if_needed(self) -> None:
while self.currsize > self.capacity:
self.remove_oldest()
def popitem(self, remove_pinned: bool = False):
"""Remove and return the `(key, value)` pair least recently used."""
if not remove_pinned:
-4
View File
@@ -8,10 +8,6 @@ from typing import Any
import torch
class AuxStreamType(Enum):
Attention = 1
class EventType(Enum):
Main = 0
Attention = 1
-34
View File
@@ -427,26 +427,6 @@ class FreeKVCacheBlockQueue:
curr_block = curr_block.next_free_block
def need_extra_keys(request: Request) -> bool:
"""Check whether the blocks allocated to this request need extra hash keys.
Args:
request (Request): The request.
Returns:
bool: Whether blocks allocated to this request need extra hash keys.
"""
# Multimodal requests need to include the MM hash.
# LoRA requests need to include the LoRA name.
# Request with provided cache salt need to include the salt.
return (
bool(request.mm_features)
or (request.lora_request is not None)
or (request.cache_salt is not None)
)
def _gen_mm_extra_hash_keys(
request: Request, start_token_idx: int, end_token_idx: int, start_mm_idx: int
) -> tuple[list[Any], int]:
@@ -1053,20 +1033,6 @@ def _get_kv_cache_groups_uniform_type(
return [KVCacheGroupSpec(list(spec.kv_cache_specs.keys()), spec)]
def is_kv_cache_page_size_uniform(kv_cache_spec: dict[str, KVCacheSpec]) -> bool:
"""
Whether all layers in the given KVCacheSpec have the same page size.
Args:
kv_cache_spec: The KVCacheSpec of each attention layer in the model
Returns:
True if all layers have the same page size, False otherwise.
"""
page_sizes = {layer.page_size_bytes for layer in kv_cache_spec.values()}
return len(page_sizes) == 1
def unify_kv_cache_spec_page_size(
kv_cache_spec: dict[str, KVCacheSpec],
) -> dict[str, KVCacheSpec]: