[Test] Add packed DeepSeek-V4 KV zeroer geometry regression (#51288)

Signed-off-by: Colton Ottley <colton@ottleyengineering.com> Co-authored-by: Colton Ottley <colton@ottleyengineering.com>

coltonottley committed Aug 7, 2026 at 13:11 UTC 0df620d429261fb4e3c1200c6b8414209587a86c
1 file changed +195
tests/v1/worker/test_dsv4_packed_zeroer_geometry.py new
+195
@@ -0,0 +1,195 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""CPU regression for the packed-DSV4 KV zeroer geometry.
4 +
5 +Constructor-crossing regression for the packed-KV zeroing stride fix (upstream
6 +PR #50276): every number below comes from instantiating the REAL production
7 +code (no arithmetic is reimplemented here):
8 +
9 + - ``vllm.v1.core.kv_cache_utils._get_packed_kv_cache_layout`` (packed
10 + offsets / block stride)
11 + - ``vllm.v1.worker.gpu.attn_utils._reshape_attention_kv_cache``
12 + (per-layer column views)
13 + - ``vllm.models.deepseek_v4.sparse_mla.DeepseekV4FlashMLABackend``
14 + (``get_kv_cache_shape`` / ``get_kv_cache_block_dim``)
15 + - ``vllm.v1.worker.utils.AttentionGroup`` / ``KVBlockZeroer``
16 + (``KVBlockZeroer.__init__`` precomputes the segment tables)
17 +
18 +CPU only: no Triton launch, no CUDA tensor, no CUDA call is made. The test
19 +asserts the packed-DSV4 constructor metadata:
20 +
21 + * block stride = the full packed row (4 aligned pages)
22 + * per-layer zero = the meaningful page (the spec's unpadded
23 + ``real_page_size_bytes``), so adjacent packed layers are
24 + never wiped
25 + * 4 packed layer views; block 99 of the highest-offset layer is the last
26 + written region and stays within the packed backing.
27 +
28 +The pre-fix constructor exposed a five-field ``_meta`` with no block stride, so
29 +its zero width equals the FULL packed row; the extraction below records that
30 +legacy defect and then asserts the fixed semantics, so failure on the old code
31 +is a semantic one (wrong zero width / out-of-bounds block 99), not a
32 +schema-shape accident.
33 +"""
34 +
35 +from types import SimpleNamespace
36 +
37 +import pytest
38 +import torch
39 +
40 +from vllm.models.deepseek_v4.sparse_mla import DeepseekV4FlashMLABackend
41 +from vllm.v1.core.kv_cache_utils import _get_packed_kv_cache_layout
42 +from vllm.v1.kv_cache_interface import MLAAttentionSpec, UniformTypeKVCacheSpecs
43 +from vllm.v1.worker.gpu.attn_utils import _reshape_attention_kv_cache
44 +from vllm.v1.worker.utils import AttentionGroup, KVBlockZeroer
45 +
46 +pytestmark = pytest.mark.cpu_test
47 +
48 +NUM_BLOCKS = 100
49 +BLOCK_SIZE_CFG = 256 # vllm_config.cache_config.block_size for DSV4 (group 0)
50 +COMPRESS = 4 # main-MLA compress_ratio
51 +STORAGE_BS = BLOCK_SIZE_CFG // COMPRESS # 64
52 +KERNEL_BS = 256 # DeepseekV4FlashMLABackend supports [256]
53 +LAST_BLOCK = NUM_BLOCKS - 1 # block 99
54 +
55 +
56 +def _extract_segments(meta, base):
57 + """Read (offset_bytes, block_stride_bytes, zero_span_bytes) per segment.
58 +
59 + Accepts both the pre-fix five-field ``_meta`` (no block stride; the zero
60 + width is the FULL packed row -- the defect) and the fixed six-field ``_meta``.
61 + All values come from the real constructor output; nothing is recomputed. Returns
62 + ``(segs, legacy_why)`` where ``legacy_why`` describes the pre-fix defect
63 + when detected.
64 + """
65 + legacy_why = None
66 + if len(meta) == 6:
67 + seg_addrs, seg_block_strides, seg_page_sizes, _max_chunks, _blk, n_segs = meta
68 + else:
69 + seg_addrs, seg_page_sizes, _max_chunks, _blk, n_segs = meta
70 + seg_block_strides = seg_page_sizes
71 + legacy_why = (
72 + f"legacy {len(meta)}-field _meta (no block stride): zero width is "
73 + f"the FULL packed row ({int(seg_page_sizes[0]) * 4} B) -- the defect"
74 + )
75 + segs = [
76 + (int(a) - base, int(bs) * 4, int(ps) * 4)
77 + for a, bs, ps in zip(seg_addrs, seg_block_strides, seg_page_sizes)
78 + ]
79 + return segs, legacy_why, n_segs
80 +
81 +
82 +def test_dsv4_packed_zeroer_geometry():
83 + """Real DSV4 constructor metadata separates block stride from zero span."""
84 + layer_names = [f"model.layers.{i}.self_attn" for i in range(4)]
85 +
86 + specs = [
87 + MLAAttentionSpec(
88 + block_size=BLOCK_SIZE_CFG,
89 + num_kv_heads=1,
90 + head_size=512,
91 + dtype=torch.uint8,
92 + compress_ratio=COMPRESS,
93 + cache_dtype_str="fp8_ds_mla",
94 + alignment=576,
95 + model_version="deepseek_v4",
96 + )
97 + for _ in layer_names
98 + ]
99 + uniform = UniformTypeKVCacheSpecs.from_specs(
100 + {ln: sp for ln, sp in zip(layer_names, specs)}
101 + )
102 + assert uniform is not None
103 +
104 + # --- Real packed layout planner -------------------------------------------
105 + block_stride, layers_by_offset = _get_packed_kv_cache_layout(
106 + [
107 + AttentionGroup(
108 + backend=DeepseekV4FlashMLABackend,
109 + layer_names=layer_names,
110 + kv_cache_spec=uniform,
111 + kv_cache_group_id=0,
112 + )
113 + ]
114 + )
115 + page_bytes = specs[0].page_size_bytes # DSV4 fp8_ds_mla page after 576B alignment
116 + real_page_bytes = specs[0].real_page_size_bytes # unpadded meaningful page
117 + alignment_gap = page_bytes - real_page_bytes # trailing planner padding
118 + total_size = block_stride * NUM_BLOCKS
119 + offsets = sorted(layers_by_offset)
120 + assert block_stride == 4 * page_bytes, (block_stride, 4 * page_bytes)
121 + assert offsets == [i * page_bytes for i in range(4)]
122 +
123 + # --- Real per-layer views (packed branch) --------------------------------
124 + backing = torch.zeros(total_size, dtype=torch.uint8)
125 + base = backing.data_ptr()
126 + views = {}
127 + for ln, off in zip(layer_names, offsets):
128 + packing = (off, block_stride)
129 + kv_shape = DeepseekV4FlashMLABackend.get_kv_cache_shape(
130 + NUM_BLOCKS, STORAGE_BS, 1, 512, cache_dtype_str="fp8_ds_mla"
131 + )
132 + stride_order = tuple(range(len(kv_shape)))
133 + view = _reshape_attention_kv_cache(
134 + backing, specs[0], kv_shape, stride_order, NUM_BLOCKS, packing
135 + )
136 + views[ln] = view
137 + assert view.data_ptr() - base == off, "data_ptr must be base+offset"
138 + assert view.stride(0) * view.element_size() == block_stride, (
139 + "row stride == block_stride (logical block stride)"
140 + )
141 +
142 + # --- Real KVBlockZeroer.__init__ (production arithmetic, CPU) ----------
143 + sctx = {ln: SimpleNamespace(kv_cache=v) for ln, v in views.items()}
144 + zeroer = KVBlockZeroer(
145 + torch.device("cpu"),
146 + attn_groups_iter=[
147 + AttentionGroup(
148 + backend=DeepseekV4FlashMLABackend,
149 + layer_names=layer_names,
150 + kv_cache_spec=specs[0],
151 + kv_cache_group_id=0,
152 + )
153 + ],
154 + kernel_block_sizes=[KERNEL_BS],
155 + cache_dtype="fp8_ds_mla",
156 + static_forward_context=sctx,
157 + )
158 + segs, legacy_why, n_segs = _extract_segments(zeroer._meta, base)
159 +
160 + # --- Fixed semantics (proven by metadata values, not by schema shape) ----
161 + assert n_segs == 4, "all 4 packed layers must register as segments"
162 + assert [off for off, _, _ in segs] == offsets, "one segment per layer offset"
163 + assert all(bs == block_stride for _, bs, _ in segs), (
164 + "block stride must be the full packed row (4 aligned pages)"
165 + )
166 + assert all(zs == real_page_bytes for _, _, zs in segs), (
167 + f"zero span must be the meaningful page ({real_page_bytes} B), got "
168 + f"{[zs for _, _, zs in segs]} B" + (f"; {legacy_why}" if legacy_why else "")
169 + )
170 +
171 + # --- Block 99 (the real final block) bounds, via constructor metadata ---
172 + # Production write for block b of a segment is
173 + # [off + b*block_stride_bytes, off + b*block_stride_bytes + zero_span_bytes).
174 + worst = 0
175 + for off, bs, zs in segs:
176 + start = off + LAST_BLOCK * bs
177 + end = start + zs
178 + oob = end - total_size
179 + worst = max(worst, oob)
180 + assert end <= total_size, (
181 + f"segment@off={off} block {LAST_BLOCK} writes [{start}, {end}) "
182 + f"-> {oob} bytes past backing ({total_size})"
183 + )
184 + assert worst == 0, f"highest-offset final block goes {worst} bytes OOB"
185 +
186 + # Highest-offset segment's block 99 is the last written region and ends exactly at
187 + # total_size - alignment_gap: the trailing planner padding stays untouched.
188 + off_last, bs_last, zs_last = segs[-1]
189 + assert off_last + LAST_BLOCK * bs_last + zs_last == total_size - alignment_gap, (
190 + "highest-offset final block must end at total_size - alignment_gap "
191 + f"({alignment_gap}-byte gap intact)"
192 + )
193 +
194 + # Zero span < block stride proves adjacent packed layers are never wiped.
195 + assert max(zs for _, _, zs in segs) < block_stride