[Bugfix] Universally align block table width to 128 tokens (#50302)

Signed-off-by: Matthew Bonanni <mbonanni@redhat.com>
This commit is contained in:
Matthew Bonanni
2026-07-31 08:52:17 -07:00
committed by GitHub
parent 94e9ef0768
commit a0cd2b69b3
8 changed files with 90 additions and 30 deletions
@@ -13,6 +13,7 @@ from vllm.v1.attention.backends.mla.indexer import (
DeepseekV32IndexerMetadataBuilder,
)
from vllm.v1.kv_cache_interface import MLAAttentionSpec
from vllm.v1.worker.block_table import get_block_table_width
def test_indexer_warmup_normalizes_zero_compress_ratios():
@@ -48,11 +49,14 @@ def test_indexer_builder_deepseek_v4_compressed_slot_mapping_uses_storage_block_
compress_ratio=4,
)
vllm_config = create_vllm_config(max_model_len=1024)
max_num_blocks = kv_cache_spec.max_num_blocks_per_req(vllm_config, 1024)
block_table_width = get_block_table_width(max_num_blocks, kv_cache_spec.block_size)
builder = DeepseekV32IndexerMetadataBuilder(
kv_cache_spec=kv_cache_spec,
layer_names=["dummy"],
vllm_config=vllm_config,
device=device,
block_table_width=block_table_width,
)
# Construct a single request where:
+3 -9
View File
@@ -50,6 +50,7 @@ from vllm.v1.kv_cache_interface import (
KVQuantMode,
MLAAttentionSpec,
)
from vllm.v1.worker.block_table import get_block_table_width
BACKENDS_TO_TEST = [
AttentionBackendEnum.CUTLASS_MLA,
@@ -1495,16 +1496,9 @@ def test_backend_correctness(
batch_spec, block_size, device
)
# Pad block table to meet requirement:
# block_num % (128 / block_size) == 0
required_divisor = int(128 / block_size)
current_block_num = common_attn_metadata.block_table_tensor.shape[1]
if current_block_num % required_divisor != 0:
# Pad to next multiple of required_divisor
padded_block_num = (
(current_block_num + required_divisor - 1) // required_divisor
) * required_divisor
padding_cols = padded_block_num - current_block_num
padded_block_num = get_block_table_width(current_block_num, block_size)
if padding_cols := padded_block_num - current_block_num:
padding = torch.zeros(
(common_attn_metadata.block_table_tensor.shape[0], padding_cols),
dtype=torch.int32,
+28
View File
@@ -46,6 +46,11 @@ from vllm.v1.kv_cache_interface import (
from vllm.v1.outputs import EMPTY_MODEL_RUNNER_OUTPUT
from vllm.v1.sample.metadata import SamplingMetadata
from vllm.v1.spec_decode.metadata import SpecDecodeMetadata
from vllm.v1.worker.block_table import (
MultiGroupBlockTable,
SlotMappingMode,
get_block_table_width,
)
from vllm.v1.worker.gpu.lora_utils import LoraState
from vllm.v1.worker.gpu.mm.encoder_cache import EncoderCache
from vllm.v1.worker.gpu.mm.lora import set_active_mm_loras
@@ -1380,6 +1385,29 @@ def test_hybrid_block_table_initialization():
)
def test_get_block_table_width_aligns_to_128_tokens():
assert get_block_table_width(1875, 64) == 1876
def test_get_block_table_width_splits_virtual_blocks():
assert get_block_table_width(235, 256, 64) == 940
def test_mamba_state_table_width_is_not_aligned():
block_tables = MultiGroupBlockTable(
max_num_reqs=1,
max_num_batched_tokens=1,
pin_memory=False,
device=torch.device("cpu"),
block_sizes=[39664],
kernel_block_sizes=[39664],
max_num_blocks=[1],
slot_mapping_modes=[SlotMappingMode.NONE],
)
assert block_tables[0].max_num_blocks_per_req == 1
def test_input_batch_with_kernel_block_sizes():
"""Test InputBatch initialization with kernel_block_sizes parameter."""
max_num_reqs = 10
+2
View File
@@ -631,6 +631,8 @@ class AttentionMetadataBuilder(ABC, Generic[M]):
# Does this backend/builder support updating the block table in existing
# metadata
supports_update_block_table: bool = False
# Whether the builder constructor requires the block-table width.
requires_block_table_width: ClassVar[bool] = False
@abstractmethod
def __init__(
+3 -11
View File
@@ -22,7 +22,6 @@ from vllm.utils.deep_gemm import (
get_paged_mqa_logits_metadata,
has_deep_gemm,
)
from vllm.utils.math_utils import cdiv
from vllm.utils.platform_utils import num_compute_units
from vllm.v1.attention.backend import (
AttentionBackend,
@@ -37,7 +36,6 @@ from vllm.v1.attention.backends.utils import (
split_decodes_and_prefills,
)
from vllm.v1.kv_cache_interface import KVCacheSpec, MLAAttentionSpec
from vllm.v1.worker.cp_utils import get_kv_cache_shard_count
logger = init_logger(__name__)
@@ -458,6 +456,7 @@ class DeepseekV32IndexerMetadataBuilder(AttentionMetadataBuilder):
# The indexer opts out of the shared reorder-threshold vote (see __init__),
# so this is None; its own split uses self.decode_threshold.
reorder_batch_threshold: int | None = None
requires_block_table_width = True
@classmethod
def get_cudagraph_support(
@@ -467,7 +466,7 @@ class DeepseekV32IndexerMetadataBuilder(AttentionMetadataBuilder):
) -> AttentionCGSupport:
return AttentionCGSupport.UNIFORM_BATCH
def __init__(self, *args, **kwargs):
def __init__(self, *args, block_table_width: int, **kwargs) -> None:
super().__init__(*args, **kwargs)
scheduler_config = self.vllm_config.scheduler_config
parallel_config = self.vllm_config.parallel_config
@@ -556,15 +555,8 @@ class DeepseekV32IndexerMetadataBuilder(AttentionMetadataBuilder):
dtype=torch.int32,
device=self.device,
)
max_num_blocks_per_req = cdiv(
self.vllm_config.model_config.max_model_len,
self.kv_cache_spec.block_size * get_kv_cache_shard_count(),
)
self.expanded_block_table_buffer = torch.zeros(
(
scheduler_config.max_num_batched_tokens,
max_num_blocks_per_req,
),
(scheduler_config.max_num_batched_tokens, block_table_width),
dtype=torch.int32,
device=self.device,
)
+32 -4
View File
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import math
from enum import Enum
import numpy as np
@@ -16,6 +17,29 @@ from vllm.v1.utils import CpuGpuBuffer
logger = init_logger(__name__)
def get_block_table_width(
max_num_blocks: int,
block_size: int,
kernel_block_size: int | None = None,
*,
token_alignment: int | None = 128,
) -> int:
"""Return the width after optional alignment and virtual block splitting."""
if kernel_block_size is None:
kernel_block_size = block_size
if block_size % kernel_block_size != 0:
raise ValueError(
f"kernel_block_size {kernel_block_size} must divide "
f"block_size {block_size} evenly"
)
if token_alignment is not None:
if token_alignment <= 0:
raise ValueError("token_alignment must be positive")
block_alignment = token_alignment // math.gcd(token_alignment, block_size)
max_num_blocks = cdiv(max_num_blocks, block_alignment) * block_alignment
return max_num_blocks * block_size // kernel_block_size
class SlotMappingMode(Enum):
TOKEN_TO_KV_SLOT = "token_to_kv_slot"
NONE = "none"
@@ -277,11 +301,15 @@ class MultiGroupBlockTable:
f"must match block_sizes length ({len(block_sizes)})"
)
# Align to a multiple of (128 / block_size) as required
# by some attention backends such as TRTLLM (#39324)
max_num_blocks = [
cdiv(n, 128 // bs) * (128 // bs) if bs <= 128 else n
for n, bs in zip(max_num_blocks, block_sizes)
(
get_block_table_width(n, block_size, token_alignment=None)
if slot_mapping_mode == SlotMappingMode.NONE
else get_block_table_width(n, block_size)
)
for n, block_size, slot_mapping_mode in zip(
max_num_blocks, block_sizes, slot_mapping_modes
)
]
self.block_tables = [
+6 -5
View File
@@ -56,6 +56,7 @@ from vllm.utils.torch_utils import STR_DTYPE_TO_TORCH_DTYPE
from vllm.v1.core.sched.output import GrammarOutput, SchedulerOutput
from vllm.v1.kv_cache_interface import KVCacheConfig, MambaSpec
from vllm.v1.outputs import DraftTokenIds, ModelRunnerOutput
from vllm.v1.worker.block_table import get_block_table_width
from vllm.v1.worker.cp_utils import check_attention_cp_compatibility
from vllm.v1.worker.gpu import pcp_manager as pcp
from vllm.v1.worker.gpu.async_utils import AsyncOutput, AsyncPoolingOutput
@@ -456,16 +457,16 @@ class GPUModelRunner(LoRAModelRunnerMixin):
max_num_blocks = cdiv(
block_table_max_model_len, spec.block_size * self.dcp_size
)
# Align to a multiple of (128 / block_size) as required by some attention
# backends such as TRTLLM (#39324)
if spec.block_size <= 128:
alignment = 128 // spec.block_size
max_num_blocks = cdiv(max_num_blocks, alignment) * alignment
# For Mamba/Hybrid Model, KVCaches need extra blocks for speculative tokens
if isinstance(spec, MambaSpec):
max_num_blocks = (
max_num_blocks if self.cache_config.enable_prefix_caching else 1
) + spec.num_speculative_blocks
max_num_blocks = get_block_table_width(
max_num_blocks, spec.block_size, token_alignment=None
)
else:
max_num_blocks = get_block_table_width(max_num_blocks, spec.block_size)
max_num_blocks_per_group.append(max_num_blocks)
self.attn_groups, attn_cg_support, self.kernel_block_sizes = init_attn_backend(
+12 -1
View File
@@ -36,6 +36,7 @@ from vllm.v1.kv_cache_interface import (
MambaSpec,
UniformTypeKVCacheSpecs,
)
from vllm.v1.worker.block_table import get_block_table_width
logger = init_logger(__name__)
@@ -237,12 +238,22 @@ class AttentionGroup:
if kernel_block_size is not None
else self.kv_cache_spec
)
builder_cls = self.backend.get_builder_cls()
builder_kwargs = {}
if builder_cls.requires_block_table_width:
max_num_blocks = self.kv_cache_spec.max_num_blocks_per_req(
vllm_config, vllm_config.model_config.max_model_len
)
builder_kwargs["block_table_width"] = get_block_table_width(
max_num_blocks, self.kv_cache_spec.block_size, kernel_block_size
)
self.metadata_builders = [
self.backend.get_builder_cls()(
builder_cls(
kv_cache_spec_builder,
self.layer_names,
vllm_config,
device,
**builder_kwargs,
)
for _ in range(num_metadata_builders)
]