Repository navigation
Conversation
Leoyzen
added a commit
to Leoyzen/sglang
that referenced
this pull request
Sep 17, 2026
…ort tests The fork increments predate sgl-project#39511's slot lifecycle, so the ported tests still encoded the old contracts and three GPU suites failed. - mamba.py LOAD: inline alloc/evict and return None when the pool stays exhausted, instead of _alloc_mamba_slot's assert. The caller already handles None by ABORTing the whole load (releasing earlier components' temporary allocations) and degrading to a plain cache miss. The tree-insert callers keep the assert. - test_mamba_direct_linker: ABORT now asserts None (the component frees the LOAD-allocated slot) and that the slot reached the allocator; full_component gains linker_indices_are_paged for the COMMIT filter. - test_unified_cache_linker_fail_soft: the fake linker implements the ABC's pop_completed_offload, and the cache fake gains the attributes main's UnifiedCacheLinkerWrapper contract reads (tree_components, components, token_to_kv_pool_allocator, tree_core helpers, canonical tail). The no-hook case shadows revalidate_load with None because del cannot remove a class attribute.
Leoyzen
added a commit
to Leoyzen/sglang
that referenced
this pull request
Sep 17, 2026
…ort tests The fork increments predate sgl-project#39511's slot lifecycle, so the ported tests still encoded the old contracts and three GPU suites failed. - mamba.py LOAD: inline alloc/evict and return None when the pool stays exhausted, instead of _alloc_mamba_slot's assert. The caller already handles None by ABORTing the whole load (releasing earlier components' temporary allocations) and degrading to a plain cache miss. The tree-insert callers keep the assert. - test_mamba_direct_linker: ABORT now asserts None (the component frees the LOAD-allocated slot) and that the slot reached the allocator; full_component gains linker_indices_are_paged for the COMMIT filter. - test_unified_cache_linker_fail_soft: the fake linker implements the ABC's pop_completed_offload, and the cache fake gains the attributes main's UnifiedCacheLinkerWrapper contract reads (tree_components, components, token_to_kv_pool_allocator, tree_core helpers, canonical tail). The no-hook case shadows revalidate_load with None because del cannot remove a class attribute.
Leoyzen
added a commit
to Leoyzen/sglang
that referenced
this pull request
Sep 18, 2026
…ort tests The fork increments predate sgl-project#39511's slot lifecycle, so the ported tests still encoded the old contracts and three GPU suites failed. - mamba.py LOAD: inline alloc/evict and return None when the pool stays exhausted, instead of _alloc_mamba_slot's assert. The caller already handles None by ABORTing the whole load (releasing earlier components' temporary allocations) and degrading to a plain cache miss. The tree-insert callers keep the assert. - test_mamba_direct_linker: ABORT now asserts None (the component frees the LOAD-allocated slot) and that the slot reached the allocator; full_component gains linker_indices_are_paged for the COMMIT filter. - test_unified_cache_linker_fail_soft: the fake linker implements the ABC's pop_completed_offload, and the cache fake gains the attributes main's UnifiedCacheLinkerWrapper contract reads (tree_components, components, token_to_kv_pool_allocator, tree_core helpers, canonical tail). The no-hook case shadows revalidate_load with None because del cannot remove a class attribute.
3 tasks done
Rebase of PR sgl-project#39511 (head 8b05571, base 4b186cf) onto current main as a single net-diff commit. The PR's 13 commits include three that supersede each other on the same DCP allocator geometry (5ed7075 -> df5cd5d -> 456b7ce); replaying them individually would have required re-resolving conflicts against the known-bad middle revision, which narrows the geometry and silently strands 7/8 of the device KV. The net diff reaches 456b7ce's final form directly. Two conflicts, both resolved keeping upstream's newer surrounding code plus this PR's semantics: - kv_cache_configurator.py: upstream c892301 introduced `paged_allocator_cls = current_platform.get_paged_allocator_cls()`. Kept that indirection and fed it the widened `loc_span` geometry rather than the raw `attn_dcp_size` multiply. - components/mamba.py: import-list collision only; upstream added `InternalStateBackup`, this PR adds `ExternalLinkerLoadPhase`. Both kept. The other eight files applied cleanly. Co-authored-by: Cursor <cursoragent@cursor.com>
Two fixtures in test_unified_cache_linker.py were written while MAMBA was rejected by the external linker and while every tree component was page-addressed. Neither held after this series: - test_cache_linker_rejects_unsupported_tree_components asserted MAMBA is refused; MAMBA is now on the supported list, so only C128 still is. - The fake component in the SWA-boundary fixture is a SimpleNamespace, so it does not inherit TreeComponent's linker_indices_are_paged and raised AttributeError once _update_load started reading it. It stands in for a paged component, so it declares True. Both tests pass on main and fail on this branch without this commit.
isytwu
force-pushed
the
k3-hybrid-mamba-linker-rebased
branch
from
October 10, 2026 03:51
8b05571 to
f0139d6
Compare
Contributor
Author
|
Superseded by #45016, which rebuilds this work on current main now that #41343 provides the Mamba linker hooks (LOAD / PREPARE / ABORT). It adds the FULL + Mamba pool group for Kimi K3, the UMBP side, DCP and the DSPARK draft KV, with unit, KL and end-to-end accuracy tests and benchmarks against HiCache. Leaving this PR open until #45016 lands. |
Contributor
Author
2 of 6 tasks
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.
Motivation
Builds on #35687 / #37578 (the external-cache linker and its UMBP/mori backend),
which are verified on GLM-5.2 and DeepSeek-V4 but not on any hybrid mamba model.
Kimi K3 is one (MLA full attention + KDA linear attention, backed by
HybridLinearKVPool), and two separate things stopped it from working:MambaComponent.build_external_linker_transferwas a stub that raisedunconditionally.
_update_load's COMMIT phase assumed every component'sdevice_indicesarepage-aligned token slots, which crashes a slot-addressed component (one state
per node, not a token range) with
shape '[-1, 64]' is invalid for input of size 1atpage_size > 1.Modifications
Hybrid mamba support (
mamba.py,linker_pool_assembler.py,hybrid_pool_assembler.py)A mamba checkpoint is one whole state slot per node, not a token range, so the
MAMBA
DevicePoolEntryis addressed by slot id over the pool's conv andtemporal buffers, alongside the ordinary paged KV entry for the full-attention
layers. Its transfer carries the single key of the node's end boundary under
TRAILING_PAGES, which is the shape the lookup's boundary filter alreadyexpects. The group is deliberately not
rank_replicated: the MLA KV isTP-replicated but the KDA state is not.
update_external_linker_loadreturns the slot allocated duringLOADwhen aload is called off; int8 mamba checkpoints are excluded, since they live in a
pool the linker's device group does not map.
Slot-addressed indices at
page_size > 1(components/base.py,unified_cache_linker.py)TreeComponent.linker_indices_are_pagednow declares whether a component'sdevice_indicesare page-aligned, and the COMMIT-phase slicing is skipped forcomponents that are not.
The COMMIT-phase test for "is this slot-addressed checkpoint still ours to
write into" is
insert_result.mamba_exist— that is exactly the conditionload_back()itself acts on: it hands the slot totree_core.insert()asInsertParams.mamba_valueand frees it there iffmamba_exist. Anadopted-range test looks like the natural fit here and is wrong:
record_adopted_range()is only ever called forBASE_COMPONENT_TYPEandSWA, never forMAMBA, so that test is always false and drops every mambaload — and in the
mamba_exist == Falsebranch the slot is already attached tothe tree node by that point, so the dropped transfer leaves the node holding a
mamba slot whose contents were never written, later used as a valid
checkpoint. Caught by per-pool load-byte counters (mamba load bytes 0 against
nonzero mamba host hits), not by any end-to-end metric, since the
restored-token count is a KV measure and says nothing about whether the
recurrent state moved.
A checkpoint that fails the test is dropped without being freed: releasing
it again through the component's
ABORTcontract would double-free a slot thetree already owns (the idle-pool invariant catches this as a mamba count one
larger than the pool,
leaked_mamba_pages=None).ABORTis for a load thatnever reached
insert.UMBPDirectLinkerload-completion tracking (umbp_direct_linker.py)Thread the request ids and a ready event through the load queue and publish
them to a completed-loads queue once a batch's layer-wise reads land,
mirroring the existing offload path.
Configuration requirement
Use this path with
page_size >= 64, not thepage_size=1thatMambaComponentotherwise forces. Atpage_size=1every token becomes itsown stored object and the restore stops paying for itself.
page_size > 1requires the mamba track buffer, i.e.
--mamba-radix-cache-strategy extra_buffer(orextra_buffer_lazy), which is what the Kimi K3 cookbookalready recommends.
Known limitation
The reachable prefix is capped at
floor((L-1)/grid)*grid, wheregrid = lcm(mamba_cache_chunk_size, page_size)— 64 for Kimi K3, whose config carriesno
mamba_chunk_sizesoFLA_CHUNK_SIZEapplies. Two rules combine:grid.match_prefixprobes0..L-1, one token short of the prompt, because arequest must keep a token to forward.
So a 16384-token prompt can restore at most 16320 tokens, and a prompt replayed
at exactly its cached length restores nothing: its only checkpoint sits at
16384, one grid step past the probe ceiling.
This all-or-nothing behaviour is correct rather than a degradation bug.
Restored KV without the matching recurrent state at that boundary is unusable,
and the state cannot be recomputed without a forward pass over the prefix, so
MAMBA's
TRAILING_PAGESfilter vetoes the boundary instead of returning KVthat cannot be used. Partial-prefix reuse, the case prefix caching exists for,
is unaffected. Raising the ceiling means changing where
MambaComponentplaces checkpoints, which is out of scope here.
Checklist
_build_dsa_device_pool_group,_build_deepseek_v4_device_pool_group) have none either; exercising this path needs a live hybrid KV pool. Will validate end to end before this leaves draft.CI States
Latest PR Test (Base): ❌ Run #38022058748
Latest PR Test (Extra): ❌ Run #38022058719
Latest PR Test (AMD ROCm 10): ❌ Run #38022058670