[sgl] Window-aware LRU refresh for SWA prefix cache in unified cache (#26615)
This commit is contained in:
@@ -8,6 +8,7 @@ from sglang.srt.mem_cache.unified_cache_components.tree_component import (
|
||||
ComponentData,
|
||||
ComponentType,
|
||||
EvictLayer,
|
||||
LRURefreshPhase,
|
||||
TreeComponent,
|
||||
get_and_increase_time_counter,
|
||||
next_component_uuid,
|
||||
@@ -20,6 +21,7 @@ __all__ = [
|
||||
"EvictLayer",
|
||||
"FullComponent",
|
||||
"CacheTransferPhase",
|
||||
"LRURefreshPhase",
|
||||
"MambaComponent",
|
||||
"SWAComponent",
|
||||
"TreeComponent",
|
||||
|
||||
@@ -19,6 +19,7 @@ from sglang.srt.mem_cache.unified_cache_components.tree_component import (
|
||||
CacheTransferPhase,
|
||||
ComponentType,
|
||||
EvictLayer,
|
||||
LRURefreshPhase,
|
||||
TreeComponent,
|
||||
next_component_uuid,
|
||||
)
|
||||
@@ -60,6 +61,31 @@ class SWAComponent(TreeComponent):
|
||||
full_indices
|
||||
)
|
||||
|
||||
def refresh_lru(
|
||||
self,
|
||||
phase: LRURefreshPhase,
|
||||
node: UnifiedTreeNode,
|
||||
root_node: UnifiedTreeNode,
|
||||
) -> None:
|
||||
match phase:
|
||||
case LRURefreshPhase.WALKDOWN:
|
||||
# Walk-down would refresh every visited ancestor to MRU,
|
||||
# but most are outside the active sliding window and must
|
||||
# stay evictable. Window-bounded refresh runs at
|
||||
# MATCH_END / INSERT_END instead.
|
||||
return
|
||||
case LRURefreshPhase.MATCH_END | LRURefreshPhase.INSERT_END:
|
||||
self.cache.lru_lists[
|
||||
self.component_type
|
||||
].reset_node_and_window_ancestors_mru(
|
||||
node,
|
||||
root_node,
|
||||
self.sliding_window_size + self.cache.page_size,
|
||||
self.node_has_component_data,
|
||||
)
|
||||
case _:
|
||||
raise ValueError(f"Unknown LRURefreshPhase: {phase}")
|
||||
|
||||
def _restore_device_value(self, node: UnifiedTreeNode, value: torch.Tensor) -> None:
|
||||
ct = self.component_type
|
||||
node.component_data[ct].value = value
|
||||
|
||||
@@ -83,6 +83,13 @@ class CacheTransferPhase(str, Enum):
|
||||
PREFETCH = "prefetch" # Storage→H
|
||||
|
||||
|
||||
class LRURefreshPhase(str, Enum):
|
||||
|
||||
WALKDOWN = "walkdown" # touching a node while walking through the tree
|
||||
MATCH_END = "match_end" # end of a successful prefix match
|
||||
INSERT_END = "insert_end" # after a new/updated leaf is committed
|
||||
|
||||
|
||||
def get_and_increase_time_counter() -> float64:
|
||||
global _LAST_ACCESS_TIME_COUNTER_FLOAT
|
||||
ret = _LAST_ACCESS_TIME_COUNTER_FLOAT
|
||||
@@ -115,6 +122,29 @@ class TreeComponent(ABC):
|
||||
value = node.component_data[self.component_type].value
|
||||
return len(value) if value is not None else 0
|
||||
|
||||
def refresh_lru(
|
||||
self,
|
||||
phase: LRURefreshPhase,
|
||||
node: UnifiedTreeNode,
|
||||
root_node: UnifiedTreeNode,
|
||||
) -> None:
|
||||
ct = self.component_type
|
||||
match phase:
|
||||
case LRURefreshPhase.WALKDOWN:
|
||||
if node.component_data[ct].value is None:
|
||||
return
|
||||
self.cache.lru_lists[ct].reset_node_mru(node)
|
||||
case LRURefreshPhase.MATCH_END:
|
||||
self.cache.lru_lists[ct].reset_node_and_parents_mru(
|
||||
node, root_node, self.node_has_component_data
|
||||
)
|
||||
case LRURefreshPhase.INSERT_END:
|
||||
# WALKDOWN already refreshed every node on the insert path
|
||||
# (including the new leaf), so there is nothing more to do.
|
||||
return
|
||||
case _:
|
||||
raise ValueError(f"Unknown LRURefreshPhase: {phase}")
|
||||
|
||||
@abstractmethod
|
||||
def create_match_validator(
|
||||
self, match_device_only: bool = False
|
||||
|
||||
@@ -44,6 +44,7 @@ from sglang.srt.mem_cache.unified_cache_components import (
|
||||
ComponentType,
|
||||
EvictLayer,
|
||||
FullComponent,
|
||||
LRURefreshPhase,
|
||||
MambaComponent,
|
||||
SWAComponent,
|
||||
TreeComponent,
|
||||
@@ -186,6 +187,24 @@ class UnifiedLRUList:
|
||||
prev_node = node
|
||||
node = node.parent
|
||||
|
||||
def reset_node_and_window_ancestors_mru(
|
||||
self,
|
||||
node: UnifiedTreeNode,
|
||||
root_node: UnifiedTreeNode,
|
||||
window_size: int,
|
||||
should_include,
|
||||
):
|
||||
prev_node = self.head
|
||||
accumulated = 0
|
||||
while node != root_node and accumulated < window_size:
|
||||
if should_include(node):
|
||||
assert node.id in self.cache
|
||||
self._remove_node(node)
|
||||
self._add_node_after(prev_node, node)
|
||||
prev_node = node
|
||||
accumulated += len(node.key)
|
||||
node = node.parent
|
||||
|
||||
def in_list(self, node: Optional[UnifiedTreeNode]):
|
||||
return node is not None and node.id in self.cache
|
||||
|
||||
@@ -800,9 +819,7 @@ class UnifiedRadixCache(KVCacheEventMixin, BasePrefixCache):
|
||||
for comp in self._components_tuple:
|
||||
if comp.component_type == BASE_COMPONENT_TYPE:
|
||||
continue # Full uses last_access_time, not LRU
|
||||
self.lru_lists[comp.component_type].reset_node_and_parents_mru(
|
||||
node_update, self.root_node, comp.node_has_component_data
|
||||
)
|
||||
comp.refresh_lru(LRURefreshPhase.MATCH_END, node_update, self.root_node)
|
||||
|
||||
cur_time = get_and_increase_time_counter()
|
||||
while node_update:
|
||||
@@ -878,7 +895,10 @@ class UnifiedRadixCache(KVCacheEventMixin, BasePrefixCache):
|
||||
def _touch_node(self, node: UnifiedTreeNode):
|
||||
node.last_access_time = get_and_increase_time_counter()
|
||||
if node != self.root_node:
|
||||
self._for_each_component_lru(node, UnifiedLRUList.reset_node_mru)
|
||||
for comp in self._components_tuple:
|
||||
if comp.component_type == BASE_COMPONENT_TYPE:
|
||||
continue
|
||||
comp.refresh_lru(LRURefreshPhase.WALKDOWN, node, self.root_node)
|
||||
|
||||
def _add_new_node(
|
||||
self,
|
||||
@@ -1014,6 +1034,15 @@ class UnifiedRadixCache(KVCacheEventMixin, BasePrefixCache):
|
||||
params=params,
|
||||
result=result,
|
||||
)
|
||||
|
||||
if target_node is not self.root_node:
|
||||
for component in self._components_tuple:
|
||||
if component.component_type == BASE_COMPONENT_TYPE:
|
||||
continue
|
||||
component.refresh_lru(
|
||||
LRURefreshPhase.INSERT_END, target_node, self.root_node
|
||||
)
|
||||
|
||||
if is_new_leaf:
|
||||
self._inc_hit_count(target_node, params.chunked)
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user