Repository navigation
[Bugfix] Pad extensible KV cache segments instead of rounding the block count - #65225
Merged
Merged
Conversation
…ck count With the extensible KV cache, any KV connector made the engine round the reserved capacity, and the count measured after warmup, down to a multiple of granule / gcd(granule, block stride) blocks. Each segment then starts on a commit granule, so a defragmenting commit can back it with one physical chunk for RDMA registration. For awkward strides that multiple is huge (2048 blocks, ~11.6 GB, for a 5,657,600 B stride), so engines with --kv-offloading-size failed to start, and registering connectors lost up to one alignment unit of KV cache. Instead, workers whose connector registers the KV cache pad each layer's region of a layer-outermost layout to a granule boundary. That costs address space only, since commits already map whole granules per segment. A defragmenting commit of any block count then backs every segment with one chunk, so the engine no longer rounds the count, or asks workers for their granule. Block-outermost layouts have a single segment at offset 0 and need no padding. Padding and the defragmenting commit apply only when a connector registers the device KV cache (registers_kv_cache_memory(): NIXL on device, Mooncake, MoRIIO and now UMBP, which registers GPU KV storage through MORI). CPU offloading copies blocks and needs neither. Fixes vllm-project#65204. Supersedes vllm-project#65210. Co-authored-by: Vegetog <110553275+Vegetog@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xgy3B2ybgXQggEmtMSLHgG Signed-off-by: Nick Hill <nickhill123@gmail.com>
njhill
requested review from
ApostaC,
ProExpertProg,
ZJY0516,
alexm-redhat,
heheda12345,
houseroad,
ivanium,
mgoin,
orozery,
robertgshaw2-redhat,
tlrmchlsmth,
yewentao256,
youkaichao and
ywang96
as code owners
November 2, 2026 20:23
WoosukKwon
approved these changes
Nov 2, 2026
5 of 6 tasks
Member
Author
|
/ci run |
|
✅ Triggered Buildkite CI #99375 for commit ℹ️ This PR is 16 commits behind upstream |
Closed
6 tasks done
6 tasks done
gcanlin
added a commit
to gcanlin/vllm
that referenced
this pull request
Nov 3, 2026
Resolves the conflict with vllm-project#65225, which added UMBPStoreConnector to the connectors that register the KV cache: keeps this branch's registers_kv_cache_memory and names UMBP in its docstring. Signed-off-by: Canlin <canlinguosdu@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 tasks done
6 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Fixes #65204. Supersedes #65210 (carries over its connector gating, with Vegetog as co-author).
With the extensible KV cache (#56492), any KV connector made the engine round the KV block count down to a multiple of
granule / gcd(granule, block_stride). For awkward strides that multiple is huge, so engines with--kv-offloading-sizecould fail to start. This PR pads segment placement instead, and applies it (and the defragmenting commit) only for connectors that register the KV cache for RDMA.Claims
--kv-offloading-size(and any connector that doesn't register the device KV cache) keeps its measured block count: no rounding, no padding, no defragmenting commit.UMBPStoreConnectoris added toregisters_kv_cache_memory(): it registers GPU KV storage through MORI. This also turns the extensible cache off for it on ROCm, as for MoRIIO.Validation
GB200, all on this branch unless noted.
Reproducer:
HuggingFaceTB/SmolLM2-360M-Instruct(20 KiB page = 2¹²·5, so main needs a multiple of 512 blocks),kv_offloading_size=1,block_size=16,max_model_len=1024,enforce_eager=True. Baseline =origin/mainversions of the changed files over the same build.gpu_memory_utilizationmax_model_len" (102 blocks rounded to 0)NIXL P/D:
tests/v1/kv_connector/nixl_integration/run_accuracy_test.shwithPREFILL_BLOCK_SIZE=16 DECODE_BLOCK_SIZE=16(Qwen3-0.6B, 1P1D): passes for the default layer-outermost layout (padded) and with--enable-cross-layers(block-outermost, single segment), 0 failed transfers. A separate DEBUG run withNixlConnectorconfirmed the padded path ran: capacity 19254 blocks (main would have rounded to 19232), NIXL registered the padded segments, and the final 18624 blocks committed exactly the nominal 31.83 GiB.Unit tests:
tests/v1/worker/test_extensible_kv_cache.py,tests/utils_/test_extensible_tensor.py,tests/v1/core/test_kv_cache_planning.py,tests/v1/engine/test_engine_args.py,tests/v1/worker/test_gpu_worker.py,tests/v1/worker/test_gpu_warmup_blocks.pyall pass. New/changed coverage:test_pad_kv_cache_segments(CPU): uniform and mixed block strides, block-outermost no-op.test_defragmenting_commit_backs_each_segment_with_one_allocation[padded]: unpadded segments still share chunks after a defragmenting commit; padded ones get one chunk each at an unaligned count.test_padded_config_allocates_and_defragments(LBNHC, BLNHC, mixed strides): views, committed views and connector placements agree on a padded reservation.test_extensible_extend_asks_connector_for_its_memory_pool: a registering connector getsdefragment=True.Details
Root cause. A connector that registers each committed segment for RDMA needs that segment backed by one physical chunk. Segments were placed at multiples of
capacity × block_stride, so they only started on commit granules when the block count was a granule multiple; the engine forced that by rounding the reserved capacity and the post-warmup count for every connector. CPU offloading only copies blocks and needs none of it.Fix.
registers_kv_cache_memory(), each worker pads its own copy of the KV cache config ininitialize_from_config(pad_kv_cache_segments): each layer region of a block-compact layer-outermost layout starts on that device's commit granule. This costs address space only; commits already map whole granules per segment (physical_bytes_for), so a defragmenting commit of any count backs each segment with exactly one chunk. Block-outermost layouts have one segment at offset 0 and are untouched. KV connectors with the extensible cache are already restricted to block-compact layouts.shrink_kv_cache_configsscalessizeby block count and scheduler-side connectors readsize // num_blocks.ExtensibleKVCachetakes the layout to derive one segment per layer region (gaps allowed) rather than inferring segments from a single stride, which can't tell padding from LHBNC head groups for small caches.align_extensible_kv_cache_capacity/granule_aligned_kv_cache_blocks, thekv_cache_commit_granuleRPC,granule_aligned_blocks/granule_block_alignment,blocks_within(aligned=).registers_kv_cache_memory()([Bugfix] Align extensible KV cache only for RDMA-registering connectors #65210).Tradeoff/limitation.
registers_kv_cache_memory()is a name-based allowlist, so out-of-tree connectors loaded viakv_connector_module_paththat register device memory no longer get one-chunk segments by default (previously any connector did). Unpadded configs (no registering connector) behave as before.Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Xgy3B2ybgXQggEmtMSLHgG