Repository navigation
Kimi-K3 plugin: LMCache MP offload with the KDA state riding the PAGE object - #3117
Conversation
…ache MP server
Kimi-K3 on the MP backend was refused ("recurrent (mamba) state offloads
in-process only"). It now takes the path DeepSeek-V4.1's checkpoints take
(#2598): the KDA state at a boundary rides the PAGE object as a native-state
image, so KV and state commit and restore as one LMCache object.
No copy is needed. vLLM lays every KV cache group out layer-outer from byte 0
of one allocation, so mamba group layer j aliases MLA plane j. On K3 the three
mamba groups of 23 KDA layers sit on the first 23 of the 29 MLA planes, and the
state a group holds in block b is PAGE unit b of those planes. A boundary's
image is one unit per mamba group (3 x 23 x 884,736 B).
- kda_ride.py: KdaRidePlanner (caps hits to the chunk grid, names the load
destination row (n - 1) // block in every mamba group or none),
kda_ride_geometry (the checkpoint spec both halves derive from vLLM's
KV cache config), publish_kda_ride_state (checks on the registered tensors
that every KDA layer starts at, and strides with, one of the leading PAGE
planes before declaring the image), and the startup refusals (align mode,
prefix caching, kv_load_failure_policy=recompute).
- V41StateMPScheduler had nothing V4.1-specific; renamed StateRideMPScheduler
(ride_mp_scheduler.py) and shared by both models. _v41_request_of is now
_request_of.
- The KDA state is per rank (TP-sharded heads); the in-process K3 leg is
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🏷️ CI GuideRuns automatically on every eligible PR before approval:
Heavy model tests:
|
valar-cop
left a comment
There was a problem hiding this comment.
some changes needed..
k3 tp8 mp boot log, and a two-pass or gsm8k showing a tier-resumed prefix matches the cold run?
ci green.
| return span * tensor.element_size() | ||
|
|
||
|
|
||
| def publish_kda_ride_state(transfer_tensors, tensors_by_group, geometry) -> None: |
There was a problem hiding this comment.
never run, so the aliasing check and the in-place kda restore have no execution behind them.. please boot k3 tp8 over mp and post a two-pass run where a tier-resumed prefix matches the cold answer
There was a problem hiding this comment.
Booted K3 TP8 over mp and ran the two-pass and a probe that reads the restored region: #3117 (comment). The aliasing check refused the first boot, which ceabea5 fixes.
| len(groups[group_id].layer_names) for group_id in group_ids | ||
| ) | ||
| plane_bytes = _layer_page_bytes(groups[attention_group_id]) | ||
| state_planes = max(layers_per_group) |
There was a problem hiding this comment.
state_planes is per mamba layer but used as attention page plane count, a dsa layer adds an index_cache plane and this breaks.. size it from the layer's movable planes instead?
There was a problem hiding this comment.
Fixed in ea0074a. The spec now sums each leading attention layer's movable bytes from vLLM's specs, folding an index cache into the layer that owns it as build_kv_cache_tensors does, and the worker counts the image's planes off the published regions' layer names. The end-to-end test also runs with a DSA index plane per layer.
| return False | ||
| if self._is_deepseek_v41: | ||
| return bool(self._v41_checkpoint_group_ids) | ||
| return bool(self._mamba_groups) |
There was a problem hiding this comment.
this now returns True for any mamba model on mp, but only kimi-k3 is named and tested.. intended? if so say the scope in the docstring, else gate on the kda mamba type
There was a problem hiding this comment.
Gated in ea0074a. K3's KDA layers report GDN_ATTN, the same mamba type as Qwen's GDN, so the gate is the kimi_k3 offload layout (text config model_type kimi_linear). Any other mamba model on mp is refused again, and the docstring names the two models that ride.
Review of #3117: - The image covered the leading N PAGE planes with N = mamba layers per group, which counts layers, not planes. An attention layer with a DSA index cache publishes two planes, so the count fell short. The spec now sums each leading attention layer's movable bytes from vLLM's specs (an index cache folded into the layer that owns it, as build_kv_cache_tensors publishes it), and the worker counts the region planes off the published regions' layer names before it checks the aliasing. - Only Kimi-K3 rides on MP. Its KDA layers report GDN_ATTN, as Qwen's GDN layers do, so the gate is the kimi_k3 offload layout; any other mamba model on MP is refused as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
valar-cop
left a comment
There was a problem hiding this comment.
one thing still open..
k3 tp8 mp boot log + a two-pass or gsm8k run where a tier-resumed prefix matches the cold run?
ci green.
| spec = geometry.spec | ||
| layers = [_published_layer(page.region) for page in pages] | ||
| state_planes = sum(1 for layer in layers if layer < state_layers) | ||
| if layers[:state_planes] != sorted(layers[:state_planes]) or any( |
There was a problem hiding this comment.
this arithmetic only ran on the hand-built cpu pool, never a real vllm layout. also the leading-run check accepts any leading plane, so 3 distinct groups vs 3 groups aliasing the same 23 planes look the same..
…E order The first K3 TP8 boot over mp refused at registration: mamba group 0 layer 18 started on PAGE plane 23, outside the image's leading 23 planes. ATOM publishes PAGE planes by layer name, while vLLM aliases mamba layer j onto the j-th attention layer it allocates, and with DSpark the two orders differ (29 attention layers, five sort ahead of the first aliased one). kda_ride_page_order moves the attention layers the mamba layers alias to the front, in mamba-layer order, matched on addresses, and the scheduler sizes the image from vLLM's allocation order. The end-to-end test gains a layer that sorts first without being aliased; without the reorder it raises the same refusal the boot did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| return span * tensor.element_size() | ||
|
|
||
|
|
||
| def publish_kda_ride_state(transfer_tensors, tensors_by_group, geometry) -> None: |
There was a problem hiding this comment.
still open from both reviews: this has never run on a real gpu/vllm layout, tests build the aliasing by hand.. need the k3 tp8 mp boot log and a two-pass run where the resumed prefix matches cold.
|
Round 3 pushed ceabea5. K3 TP8 boots over mp; tier-resumed prefixes match the cold run.
Why, and what ranThe first boot stopped with mamba group 0 layer 18 starting on PAGE plane 23. vLLM aliases mamba layer j onto the j-th attention layer it allocates, while PAGE planes were published by layer name, and with DSpark 5 of the 29 attention layers sort ahead of the aliased ones. ceabea5 publishes the aliased layers first and sizes the image in vLLM's order; the end-to-end test now has a layer that sorts first without being aliased. Runs: this head plus #3137's one-line
The gsm8k miss is degenerate from its first token (run 1, question 10); the same question resumed from the tier is correct in the four other passes. The off arm has a comparable event with no tier involved: a cold probe answers a wrong code, its right token falling from -0.03 to -1.86. Both read as the K3 plugin's base rate at concurrency 16. No load failure, withdrawn promise or tier error in any run. EvidenceBoot (every on run, 8 ranks each): Where the cached prefix came from, per later pass (vLLM metrics, deltas): on, probe 737,280 of 903,856 prompt tokens from the tier and 0 from HBM; on, gsm8k 837,120 of 1,007,813 from the tier and 0 from HBM; off, 0 from either. Runs (probe passes at concurrency 16 except the first two, 32 prompts one at a time; gsm8k 80 questions at concurrency 16):
|
|
ci triage for ceabea5:
|
#2365 made the shared K3 KDA forward read `kda_metadata.spec_state_indices_in_tensor` on its speculative-decode branch. vLLM's KimiK3KDAMetadata has no such field, and the plugin only defaulted its non-spec twin, so K3 on the plugin dies in profiling under DSpark (AttributeError on the first verify-shaped forward). None keeps the forward on the write slot, as the non-spec default does. The same hunk as #3137's, so the two apply cleanly in either order. The K3 TP8 LMCache MP runs on this PR ran with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main renamed V41StateMPScheduler to StateRideMPScheduler (#3117); the plugin test fixture keeps the token shipper it needs and uses the new class.
Ready for review. No blocking issues.
Kimi-K3 on the vLLM plugin could offload only in-process; the MP backend refused it. This follows #2598: the KDA state at a boundary rides the PAGE object as a native-state image, so the KV and the state are one LMCache object.
Dev record
kimi_k3offload layout (its KDA layers reportGDN_ATTN, as Qwen's GDN does).V41StateMPScheduleris nowStateRideMPScheduler, shared by both models.spec_state_indices_in_tensordefault to start, with or without this PR.Named result
Kimi-K3 offloads over the LMCache MP server, given
--enable-prefix-caching,--mamba-cache-mode align,kv_load_failure_policy: recomputeand an LMCache chunk that is a multiple of 1536. The in-process K3 path is unchanged.Gates
branch @ ceabea5: K3, V4.1 and planner plugin tests 109 passed, 1 failed (fails the same on main). ruff, black clean.
K3 TP8 over mp: tier-resumed probe 736/736 vs recomputed 703/704; gsm8k later passes 399/400 vs 320/320.
Evidence
test_decode_keeps_are_stored_by_the_next_build_before_it_stages:HIP error: no ROCm-capable device is detectedin the GPU-less test container.test_the_state_rides_as_the_native_layout_registers_itbuilds the layout withbuild_native_state_mp_layoutover CPU storage aliased the way vLLM allocates it (MLA only, MLA with a DSA index plane, and a layer that sorts first without being aliased) and checks byte by byte that image unit j holds mamba group j's state.🤖 Generated with Claude Code