Reset only the used mamba state on unified radix cache (#31648)

This commit is contained in:
Ke Bao
2026-07-18 21:43:13 +08:00
committed by GitHub
parent d7b9425529
commit 48ae829f6e
2 changed files with 97 additions and 0 deletions
@@ -23,6 +23,7 @@ from sglang.srt.mem_cache.unified_cache_components.tree_component import (
CacheTransferPhase, CacheTransferPhase,
ComponentType, ComponentType,
EvictLayer, EvictLayer,
LRURefreshPhase,
TreeComponent, TreeComponent,
get_and_increase_time_counter, get_and_increase_time_counter,
) )
@@ -56,6 +57,30 @@ class MambaComponent(TreeComponent):
# HiCache state # HiCache state
self._mamba_pool_host = None # set to host mamba pool when HiCache enabled self._mamba_pool_host = None # set to host mamba pool when HiCache enabled
def refresh_lru(
self,
phase: LRURefreshPhase,
node: UnifiedTreeNode,
root_node: UnifiedTreeNode,
) -> None:
# A match consumes only best_match_node's mamba state (cf. inc_lock_ref,
# which locks just this node's mamba value), unlike Full whose whole matched
# path is reused as prefix. Refreshing ancestors would keep a whole session's
# states adjacent in the mamba LRU and evict cold sessions wholesale, so touch
# only the used state. New leaf states enter the LRU via
# commit_insert_component_data, so the insert walk (WALKDOWN) is a no-op here.
ct = self.component_type
match phase:
case LRURefreshPhase.WALKDOWN:
return
case LRURefreshPhase.MATCH_END:
if node.component_data[ct].value is not None:
self.cache.lru_lists[ct].reset_node_mru(node)
case LRURefreshPhase.INSERT_END:
return
case _:
raise ValueError(f"Unknown LRURefreshPhase: {phase}")
def create_match_validator( def create_match_validator(
self, match_device_only: bool = False self, match_device_only: bool = False
) -> Callable[[UnifiedTreeNode], bool]: ) -> Callable[[UnifiedTreeNode], bool]:
@@ -4053,6 +4053,78 @@ class UnifiedLRUListBoundedRefreshTest(CustomTestCase):
self.assertEqual(self._lru_order(lru), before) self.assertEqual(self._lru_order(lru), before)
class TestUnifiedMambaLRUMatchRefresh(CustomTestCase):
"""A prefix-cache hit must refresh only best_match_node's mamba state in the
mamba LRU, not its ancestors. The base TreeComponent refresh is whole-chain
(reset_node_and_parents_mru), which clusters a session's states so that under
mamba-pool pressure eviction drops whole cold sessions instead of the
intermediate states reuse never needs. Guards MambaComponent.refresh_lru's
single-node MATCH_END override.
"""
cfg = CacheConfig(page_size=1, components=(ComponentType.FULL, ComponentType.MAMBA))
def _mamba_lru_mru_to_lru(self, cache):
lru = cache.lru_lists[ComponentType.MAMBA]
pt = lru._pt
out, cur = [], lru.head.lru_next[pt]
while cur is not lru.tail:
out.append(cur)
cur = cur.lru_next[pt]
return out
def _make_req(self, req_to_token_pool):
req = Req(
rid=0,
origin_input_text="",
origin_input_ids=array("q"),
sampling_params=SamplingParams(temperature=0, max_new_tokens=1),
)
req_to_token_pool.alloc([req])
return req
def test_match_refreshes_only_used_node(self):
cache, allocator, req_to_token_pool = build_fixture(self.cfg)
def insert(tokens):
value = allocator.alloc(len(tokens))
req = self._make_req(req_to_token_pool)
cache.insert(
InsertParams(
key=RadixKey(array("q", tokens)),
value=value[: len(tokens)],
mamba_value=req.mamba_pool_idx.unsqueeze(0),
)
)
def match_leaf(tokens):
return cache.match_prefix(
MatchPrefixParams(key=RadixKey(array("q", tokens)))
).best_match_node
# Two independent sessions, each a 2-node mamba chain:
# root -> a1 -> b1 and root -> a2 -> b2
insert([1, 2, 3])
insert([1, 2, 3, 4, 5, 6])
insert([7, 8, 9])
insert([7, 8, 9, 10, 11, 12])
b1 = match_leaf([1, 2, 3, 4, 5, 6])
a1 = b1.parent
b2 = match_leaf([7, 8, 9, 10, 11, 12])
a2 = b2.parent
# Session 2 matched last, so session 1's ancestor a1 is older than a2.
order = self._mamba_lru_mru_to_lru(cache)
self.assertGreater(order.index(a1), order.index(a2))
# Re-access session 1: only its consumed leaf b1 moves to MRU; ancestor a1
# must stay put -- whole-chain reset would bump a1 ahead of a2.
self.assertIs(match_leaf([1, 2, 3, 4, 5, 6]), b1)
order = self._mamba_lru_mru_to_lru(cache)
self.assertIs(order[0], b1)
self.assertGreater(order.index(a1), order.index(a2))
class TestUnifiedRadixCacheInt8MambaCheckpoint(CustomTestCase): class TestUnifiedRadixCacheInt8MambaCheckpoint(CustomTestCase):
cfg = CacheConfig( cfg = CacheConfig(
components=(ComponentType.FULL, ComponentType.MAMBA), components=(ComponentType.FULL, ComponentType.MAMBA),