Remove swa and mamba radix cache (#40313)

This commit is contained in:
Ke Bao
2026-09-20 16:16:27 +08:00
committed by GitHub
parent 5c69e32abe
commit a8a4d86be9
24 changed files with 69 additions and 5184 deletions
+4 -6
View File
@@ -72,12 +72,10 @@ def handle_mamba_backend(server_args: Any):
def handle_int8_mamba_checkpoint(server_args: Any):
# The int8 mamba checkpoint pool is only wired into the built-in
# MambaRadixCache. The host-offload path (enabled by
# --enable-hierarchical-cache) and custom radix-cache backends are NOT
# int8-aware: they would read int8 checkpoint slots as bf16 active slots
# (wrong pool / out-of-range). Reject the combination up front rather than
# silently corrupting state.
# The host-offload path (enabled by --enable-hierarchical-cache) and
# custom radix-cache backends are NOT int8-aware: they would read int8
# checkpoint slots as bf16 active slots (wrong pool / out-of-range).
# Reject the combination up front rather than silently corrupting state.
cfg = resolving_view(server_args)
if not cfg.enable_int8_mamba_checkpoint:
return
@@ -25,7 +25,7 @@ def _qwen4_exp_overrides(server_args: Any, hf_config: Any) -> dict:
"""Compressed QSA must own ``page_size`` here,
so the qwen3_5 hybrid attention-shape policy is restated rather than shared.
page_size=64 needs page-aligned full-KV allocation (slots are full_slot // ratio),
which MambaRadixCache allows only with mamba extra-buffer or --disable-radix-cache.
which in turn needs the mamba extra-buffer strategy or --disable-radix-cache.
"""
cfg = resolving_view(server_args)
if (
-3
View File
@@ -678,9 +678,6 @@ class Envs:
SGLANG_ENABLE_UNIFIED_RADIX_TREE = EnvBool(False)
# Registered TreeCore backend serving the unified radix cache.
SGLANG_UNIFIED_RADIX_TREE_CORE_BACKEND = EnvStr("python")
# TODO(DSV4): @ispobock this has bug on main branch when retract
SGLANG_OPT_SWA_RADIX_CACHE_COMPACT = EnvBool(False)
SGLANG_OPT_SWA_SPLIT_LEAF_ON_INSERT = EnvBool(False)
SGLANG_OPT_SWA_RELEASE_LEAF_LOCK_AFTER_WINDOW = EnvBool(False)
# ===================================================================
@@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Any
import torch
from sglang.srt.mem_cache.radix_cache import RadixCache
from sglang.srt.mem_cache.swa_radix_cache import SWARadixCache
from sglang.srt.mem_cache.unified_cache.unified_tree_core_interface import (
RadixCacheWalkResult,
)
@@ -34,7 +33,7 @@ def walk_radix_cache_for_canary(
return radix_cache.tree_core.walk_for_kv_canary(
unlocked_only=unlocked_only, swa_resident_only=swa_resident_only
)
if cache_type is not RadixCache and cache_type is not SWARadixCache:
if cache_type is not RadixCache:
raise NotImplementedError(
f"walk_radix_cache_for_canary does not support {cache_type.__name__}"
)
@@ -133,9 +132,6 @@ def _node_is_unlocked_for_canary(
if type(radix_cache) is RadixCache:
return node.lock_ref == 0
if type(radix_cache) is SWARadixCache:
return node.full_lock_ref == 0
raise NotImplementedError(
f"walk_radix_cache_for_canary does not support {type(radix_cache).__name__}"
)
@@ -146,7 +142,5 @@ def _node_is_swa_resident_for_canary(
node: TreeNode,
radix_cache: BasePrefixCache,
) -> bool:
if type(radix_cache) is SWARadixCache:
return not node.swa_tombstone
# RadixCache has no SWA tier, so every node it holds is resident.
return True
@@ -99,8 +99,8 @@ class SchedulerInvariantChecker:
session_held = self.pool_stats_observer.session_held_full_tokens()
total = ps.full_capacity
elif self.is_hybrid_ssm:
# Branch on cache type for the protected accessor (MambaRadixCache
# splits full/mamba; ChunkCache only has the single protected_size).
# Branch on cache type for the protected accessor (a mamba-capable
# cache splits full/mamba; ChunkCache only has the single protected_size).
# Use the allocator's `.size` for `total`: static max_total_num_tokens for
# non-unified pools, the dynamic byte-coordinated cap (matching
# `available_size`) for the unified pool.
+1 -1
View File
@@ -38,7 +38,7 @@ to keep. The layout is specified in
Two groups sit outside that stack:
- **Radix cache** is its own axis. The per-model variants (`radix_cache.py`,
`swa_radix_cache.py`, `mamba_radix_cache.py`, `hiradix_cache.py`, `chunk_cache.py`)
`hiradix_cache.py`, `chunk_cache.py`)
are converging onto the **Unified Radix Cache** (`unified_cache/`,
[#20415](https://github.com/sgl-project/sglang/issues/20415)), whose Full/SWA/Mamba
component model is documented in
+2 -2
View File
@@ -271,10 +271,10 @@ def retraction_discard(req: Req, tree_cache: BasePrefixCache, backend: str) -> N
def release_kv_cache(req: Req, tree_cache: BasePrefixCache, is_insert: bool = True):
assert (not req.kv.holds_kv) == req.kv.is_kv_released
# MambaRadixCache may alloc mamba state before alloc KV cache
# A mamba-capable cache may alloc mamba state before alloc KV cache
if not req.kv.holds_kv:
assert tree_cache.supports_mamba(), (
"Only MambaRadixCache allow freeing before alloc"
"Only a mamba-capable tree cache allows freeing before alloc"
)
# TODO (csy, hanming): clean up this early allocation logic
if req.kv.holds_mamba:
File diff suppressed because it is too large Load Diff
@@ -52,8 +52,8 @@ class PureSWARadixCache(RadixCache):
return 0
def sanity_check(self):
"""No-op: PureSWARadixCache uses RadixCache's simple tree structure
which doesn't need the dual-LRU sanity checks of SWARadixCache."""
"""No-op: an all-SWA model has no full tier, so there is no full/SWA
split to cross-check."""
pass
def evict(self, params: EvictParams) -> EvictResult:
+2 -2
View File
@@ -74,8 +74,8 @@ class QSATokenToKVPool(HybridLinearKVPool):
"compressed QSA requires a paged full-KV cache with the page "
"a multiple of the compress ratio (compressed slots are "
f"full_slot // ratio): page_size={page_size}, "
f"ratio={qsa_compress_ratio}. With MambaRadixCache this "
"needs the mamba extra-buffer strategy or "
f"ratio={qsa_compress_ratio}. This needs the mamba "
"extra-buffer strategy or "
"--disable-radix-cache (see the Qwen4-Exp arg overrides)."
)
# super().__init__ computes mem_usage via the overridden get_kv_size_bytes,
File diff suppressed because it is too large Load Diff
@@ -4,7 +4,7 @@ A component-based, pluggable prefix cache framework for SGLang that unifies Full
## Design Goals
1. **Unified tree structure** — One radix tree manages all KV cache types instead of separate specialized implementations (`SWARadixCache`, `MambaRadixCache`, etc.).
1. **Unified tree structure** — One radix tree manages all KV cache types, replacing the separate specialized implementations that preceded it.
2. **Pluggable components** — Each attention/state type (Full, SWA, Mamba) is a `TreeComponent` that implements hook interfaces. Adding a new cache type only requires adding a new component.
3. **Per-component resource isolation** — Each component has its own lock reference counting, evictable/protected size tracking, and eviction driver. Auxiliary components use per-component LRUs; Full uses device/host leaf sets.
4. **Cascade eviction with priority** — When a component evicts a node, lower-or-equal-priority components on the same node are evicted together, maintaining cross-component consistency.
@@ -537,8 +537,7 @@ class MambaComponent(TreeComponent):
# slot's unflushed ring depth (`write_pos`), so on request finish cap
# the donate to the last flush boundary (where temporal is current)
# and reset the cursor, keeping the donated checkpoint consistent with
# its key length. page_size is asserted == 1, so no realign. Mirrors
# MambaRadixCache.cache_finished_req.
# its key length. page_size is asserted == 1, so no realign.
if is_finished:
write_pos_buf = (
self.cache.req_to_token_pool.mamba_pool.replayssm_write_pos
+1 -1
View File
@@ -4611,7 +4611,7 @@ def get_extend_input_len_swa_limit(
sliding_window_size: int, chunked_prefill_size: int, page_size: int
) -> int:
# 1. a factor of 2x is because each prefill contains chunked_prefill_size tokens,
# and between prefills, we run swa_radix_cache.cache_unfinished_req(),
# and between prefills, we run the tree cache's cache_unfinished_req(),
# so we unlock the previously locked nodes.
# 2. max is to handle the case that chunked_prefill_size is larger than sliding_window_size.
# in that case, each prefill contains chunked_prefill_size tokens,
@@ -2,7 +2,6 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Any, Callable, Dict
from sglang.srt.mem_cache.swa_radix_cache import TreeNode as SWATreeNode
from sglang.srt.mem_cache.unified_radix_cache import UnifiedTreeNode
if TYPE_CHECKING:
@@ -18,8 +17,6 @@ def get_all_node_lock_refs(ctx: ScriptedContext) -> Dict[int, int]:
def _node_lock_ref(node: Any) -> int:
if isinstance(node, SWATreeNode):
return node.full_lock_ref + node.swa_lock_ref
if isinstance(node, UnifiedTreeNode):
return sum(cd.lock_ref for cd in node.component_data)
return node.lock_ref