Skip to content

[KV Cache] GLM-5.3-Flash: use the generic packed KV layout; CircularBufferSpec tail with speculative ring slots - #55219

Draft
ivanium wants to merge 4 commits into
vllm-project:mainfrom
ivanium:refactor/glm-release
Draft

[KV Cache] GLM-5.3-Flash: use the generic packed KV layout; CircularBufferSpec tail with speculative ring slots#55219
ivanium wants to merge 4 commits into
vllm-project:mainfrom
ivanium:refactor/glm-release

Conversation

@ivanium

@ivanium ivanium commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Purpose

Follow-up to #53906 (GLM-5.3-Flash). Remove the model-specific KV cache grouping / tensor layout and the matching NIXL and Mooncake special cases by routing GLM through the generic packed (block-outermost) layout that DeepSeek-V4 and Qwen3.8-Next already use, and fix two issues in the kpool tail ring.

  • KpoolTailSpec is dropped; the tail is a plain CircularBufferSpec (its manager already was CircularBufferManager).
  • The ring is sized for speculative tokens, kpool * cdiv(kpool + num_spec, kpool) as the QSA ring does. With exactly kpool slots, a rejected pool-completing draft left the drafts behind it overwriting the committed keys the redo needs (covered by new CUDA kernel and index-level tests). The seed/decode tail kernels (NVIDIA and AMD) take the ring size and the tail view's strides.
  • GLM's MLA and indexer pages have incompatible sizes, which _get_kv_cache_groups_glm5_next / _glm5_next_tensor_layout handled with a hand-rolled layer-outer layout, storage_block_size, worker-side virtual block splitting and NIXL "dense virtual transfer pages". The generic packed path already handles mixed page sizes, but a block-outermost block cannot be split by the worker into the 32/64-row pages DeepGEMM paged-MQA and the TRT-LLM sparse MLA kernel take. This PR re-pages inside the kernels' callers instead: kpool_page_geometry / _kpool_flat_page_view / _indexer_page_table for the indexer (fed by a block_stride_bytes builder hook), _kernel_paged_view for the FlashInfer sparse MLA impls (TRT-LLM on SM100 now MultipleOf(32), 32-row pages; SM120 64-row pages; SM90 uses page size 1 so it only needs the row stride and no longer forces the LBHNC layout), and the packed block stride is aligned to the lcm of the kernel pages (_packed_block_alignment_bytes, only when a layer needs re-paging; DeepSeek-V4 and Qwen are unaffected). The page size a backend re-pages to is declared by a new AttentionBackend.get_kernel_page_rows() and recorded on MLAAttentionSpec.kernel_page_rows, so the layout code does not hard-code kernel constants. GLM keeps block 1152 and 3 Mamba groups; the alignment costs ~4.7% KV capacity on GB200 TP4.
  • Net: 34 files, +750/-1303. kv_cache_utils.py and the NIXL worker carry no GLM-specific code path any more. The NIXL wire layout is unchanged (the removed virtual pages were never part of a release), so no NIXL_CONNECTOR_VERSION bump.

Duplicate check: no open PR touches the GLM KV layout, KpoolTailSpec, storage_block_size or the NIXL virtual transfer pages.

Test Plan

pytest tests/v1/core/test_kv_cache_utils.py tests/v1/core/test_contiguous_kv_packing.py \
  tests/v1/attention/test_kpool_page_geometry.py tests/v1/attention/test_kpool_tail_slot_mapping.py \
  tests/v1/kv_connector/unit/test_nixl_desc_geometry.py tests/v1/kv_connector/unit/test_mooncake_store_hma_e2e.py \
  tests/v1/core/prefix_cache/test_partial_prefix_cache_hits.py tests/v1/worker/test_attn_utils.py \
  tests/v1/attention/test_sparse_indexer_decode_seq_lens.py tests/v1/attention/test_indexer_deepseek_v4_slot_mapping.py \
  tests/kernels/test_kpool_decode_update_batched.py tests/v1/attention/test_flashinfer_mla_sparse_sm90.py \
  tests/v1/attention/test_sparse_mla_backends.py

GLM-5.3-Flash, TP4-to-TP4 NIXL PD on GB200, GSM8K 5-shot x2 (mqa), no MTP:

Test Result

AI assistance (Claude Code) was used for analysis, implementation and review; the submitter reviewed every line.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The kpool tail is a one-block-per-request ring addressed by ``pos % ring``,
which is exactly what CircularBufferSpec / CircularBufferManager already
implement for Qwen's QSA ring. KpoolTailSpec only added a SlidingWindowSpec
base (dragging in extra_retained_tokens tagging, sliding-window offload
sizing and cascade heuristics for a 4-token "window") and an empty manager
subclass. Drop both; the grouping code and the NIXL / Mooncake workers key
on CircularBufferSpec instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
Each MTP step stashes 1 + num_speculative_tokens rows into the tail ring
before acceptance is known. With a ring of exactly ``index_kpool`` slots the
drafts that follow a pool-completing draft overwrite the committed keys of
that pool, so when the completing draft is rejected the redo recompresses
the boundary pool from wrong keys. Size the ring as
``kpool * cdiv(kpool + num_spec, kpool)`` (the QSA rule) and address it by
``pos % ring`` while the pool phase stays ``pos % kpool``.

The seed and decode kernels take the ring size and the tail view's block and
head strides explicitly, so the tail no longer has to be a dense
``[num_blocks, 2, ring, head_dim]`` allocation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
GLM-5.3-Flash carried its own KV cache grouping and tensor layout
(``_get_kv_cache_groups_glm5_next`` / ``_glm5_next_tensor_layout``) plus
matching special cases in the NIXL and Mooncake workers. It needed them
because the MLA and indexer pages have incompatible sizes, which the generic
code already handles by packing pages side by side inside each block
(``_get_packed_kv_cache_groups``, as DeepSeek-V4 and Qwen3.8), but that
layout is block-outermost and the worker cannot virtually split such a block
into the 32/64-row pages the DeepGEMM paged-MQA and TRT-LLM sparse MLA
kernels take. Re-page inside the kernels' callers instead:

* The kpool indexer views its block as consecutive kernel pages with a
  stride in pages between blocks (``kpool_page_geometry``,
  ``_kpool_flat_page_view``) and the metadata builder expands the block
  table and slot mapping to those pages (``_indexer_page_table``), fed by a
  new ``block_stride_bytes`` builder hook. ``Glm5NextIndexerBackend`` takes
  the manager block as its kernel block and packed layouts.
* The TRT-LLM sparse MLA backend accepts any multiple of 32 rows and
  re-pages larger blocks into ``MLA_KERNEL_PAGE_ROWS``-row pages
  (``_kernel_paged_view``); top-k indices are flat row ids so paging is
  free to change.
* The packed block stride is rounded to the lcm of the re-paged kernel
  pages (``_packed_block_alignment_bytes``). With GLM's 1024-byte NoPE MLA
  rows and 132-byte indexer rows that is 1.03 MB per 13.4 MB block, about
  4.7% of KV capacity.

This removes ``storage_block_size`` and its plumbing, the GLM grouping and
layout code, the NIXL virtual-transfer-page and covered-view handling, and
the Mooncake tail rule. GLM keeps block 1152 and 3 Mamba groups; validated
on TP4-to-TP4 NIXL PD with GSM8K 0.9727 / 0.9742.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
``MambaSpec.real_page_size_bytes`` has no caller once the GLM tensor layout
is gone, and the manager module's logger was never used.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
@ivanium
ivanium force-pushed the refactor/glm-release branch from e79dc1e to 6712aa1 Compare September 4, 2026 06:16
@mergify

mergify Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @ivanium.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@200lz

200lz commented Sep 6, 2026

Copy link
Copy Markdown

I'm preparing a focused fix for ordinary 64-row V3.2 sparse MLA/indexer packing. At the current head, #55219's packed alignment is tied to kernel re-paging, while ordinary heterogeneous row widths still require a common packed-block row alignment. Would it make sense for a shared alignment helper to carry both requirements?

@200lz

200lz commented Sep 6, 2026

Copy link
Copy Markdown

Opened #55528 with the focused ordinary-row alignment change. Happy to coordinate the shared allocator helper if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants