Repository navigation
[Core] Prefer a block-outermost KV cache layout when layers can be packed - #58380
LucasWilkinson wants to merge 5 commits into
Conversation
A single-layer bucket, typically a speculative drafter layer, drove the hybrid grouping heuristic to a group size of 1, giving one KV cache group per layer. Fall back to the multi-layer group size with the least padding in that case; other models keep their existing grouping. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Replace the min / 1.5x-max heuristic with a search over group sizes from the smallest bucket (at least 2) to the largest, minimizing padding with full attention padding weighted over sliding window / mamba padding. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
…se bytes Bound the hybrid KV cache group size below by min_kv_cache_group_layers (default 3) and cost padding layers by each spec's max_memory_usage_bytes instead of fixed full vs. other weights. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
…cked When KV cache specs have mixed page sizes, block-outermost layouts let _get_packed_kv_cache_groups pack them into one block instead of padding to a common page and splitting into more groups. Prefer them when every attention backend accepts each layer's block size without kernel-block splitting, which block-outermost layouts cannot do. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Compute both groupings from the specs and prefer a block-outermost layout only if the layer-outermost grouping would pad full attention, or packing needs fewer KV cache groups. Keeps MiMo-V2.5 + DFlash at 7 LBHNC groups instead of 12 packed groups. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
tomasruizt
left a comment
There was a problem hiding this comment.
What should happen in pipeline parallelism (assume two stages are assigned different layers), and they compute different preferences? e.g. one stage prefers packed layout and the other doesn't. Atm the function resolve_kv_cache_layout() raises an AssertionError when the supported layouts of each rank are ordered differently.
vllm/vllm/v1/attention/backends/utils.py
Lines 285 to 288 in 741edee
We could compute the intersection of the layouts across all ranks, and then e.g. use the ordering of rank0 as an ordering heuristic.
# intersection
common = reduce(set.intersection, [set(l) for l in supported_layouts])
if len(common) == 0:
raise ValueError("No KV cache layout is supported by every worker.")
# ordering heuristic: use rank0 preferences
candidates = [
_layout_from_name(name)
for name in supported_layouts[0]
if name in common
]Code to reproduce the issue:
- Refactor to pure functions: tomasruizt@c3a53da
- Add failing test case: tomasruizt@b1c5f28
- Suggested fix: tomasruizt@adb5936
|
This pull request has merge conflicts that must be resolved before it can be |
|
Another case: Currently, the packed layout is preferred when uniform grouping pads full-attentions layers, even when packing more more memory. We could instead compare worst-case allocation for both layouts.
|
Purpose
When KV cache specs have mixed page sizes, e.g. the DFlash drafter for Qwen3.6-35B-A3B (4 KiB/token/layer) next to the target (2 KiB/token/layer), the default layer-outermost layout unifies page sizes by shrinking the drafter's block size, which keeps target and drafter layers in separate buckets and forces padding and extra KV cache groups (#58207 picks 17 groups with 12 KiB/token of padding at the default
--min-kv-cache-group-layers 3).A block-outermost layout (
BLHNC/BLNHC) lets_get_packed_kv_cache_groupspack layers with different page sizes into one block instead: the drafter's full attention layer occupies 2 page slots in the same group as the target's 10 full attention layers. For Qwen3.6-35B-A3B + DFlash this gives 5 groups and no padding (3 mamba, 1 full attention = 10 target + 1 drafter, 1 drafter sliding window), with slightly more KV cache capacity than main's 46 groups.#58207 uniform-page layout (LBHNC), gs=3: 17 groups
this PR, packed layout (BLNHC): 5 groups. Each row is a separate group; every group's block is the same size (12 pages of 640 tokens × 2 KiB = 15 MiB)
Group counts, block sizes and KV capacity are from the served runs below. The per-group layer split is from
_get_packed_kv_cache_groups(not logged).Block-outermost layouts cannot split a manager block into smaller kernel blocks (
create_kv_cache_viewsrequires dense pages for that). So this PR only changes the worker's reported layout preference, moving block-outermost layouts first, when:the worker's KV cache specs have mixed page sizes (when the packed path applies), and
every attention backend accepts each attention layer's manager block size as a kernel block (no splitting).
the
BLHNCcost check passes: both groupings are computed from the specs, and block-outermost is preferred only if the layer-outermost grouping would pad full attention, or packing needs fewer KV cache groups.BLHNCis not always better: for MiMo-V2.5 + DFlash packing gives 12 groups vs 7, with no full attention padding to remove.Otherwise the preference order is unchanged.
VLLM_KV_CACHE_LAYOUTand KV connector layout preferences still take precedence.On B300, FlashInfer (16/32/64-token kernel blocks) and FA4's hd256 kernel (128-token pages) need the 640/1280-token hybrid manager blocks split, so with default backends nothing changes;
TRITON_ATTN(any multiple of 16) gets the packed layout automatically. Making FlashInfer/FA work with packed blocks needs in-kernel re-paging, similar to what #55219 / #57169 do for sparse MLA.Why not duplicate: #55219 / #57169 route GLM-5.3-Flash through the packed layout and re-page for sparse MLA kernels; #54795 / #55340 change how per-worker / draft layout sets are intersected. None prefers block-outermost layouts automatically for mixed-page-size models.
Test Plan
End to end, Qwen3.6-35B-A3B-FP8 + z-lab/Qwen3.6-35B-A3B-DFlash, 1× B300, BF16 mamba states, GSM8K, c=1..32, no
VLLM_KV_CACHE_LAYOUT:vllm serve Qwen/Qwen3.6-35B-A3B-FP8 --max-num-seqs 32 --no-enable-prefix-caching --block-size 128 \ --attention-backend TRITON_ATTN --mamba-cache-dtype bfloat16 --mamba-ssm-cache-dtype bfloat16 \ --speculative-config '{"model":"z-lab/Qwen3.6-35B-A3B-DFlash","method":"dflash","num_speculative_tokens":7,"attention_backend":"TRITON_ATTN"}'Test Result
test_prefers_packed_kv_cachecovers mixed / uniform page sizes and a backend that would need kernel-block splitting. All oftests/v1/worker/test_utils.pypasses.With the same Triton backend, the packed layout improves output throughput by ~4% at c=1 and ~12% at c=32 over #58207's grouping, and recovers the KV cache capacity #58207 spends on padding. Acceptance length is 5.1–5.2 in all runs (single runs).
Cost check, served with default backends (no
VLLM_KV_CACHE_LAYOUT):TRITON_ATTNBLNHCLBHNC(can't pack: kernel-block split)LBNHC(cost check fails: 12 vs 7 groups, no full attn padding)See the RFC for the full design: #58638.
AI assistance (Claude Code) was used; I have reviewed the change.
🤖 Generated with Claude Code