Skip to content
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
0d2651c
Init support for himamba tree offloading
hzh0425 Mar 12, 2026
638ff30
Fix mamba prefix match logic
hzh0425 Mar 13, 2026
3556ed6
Enhance evict logic & Loading host mamba into both req and node's mam…
hzh0425 Mar 14, 2026
282737a
Fix hang issue
hzh0425 Mar 15, 2026
f2a0629
Refactor prefetch logic; fix memory leak issue
hzh0425 Mar 15, 2026
7316d0e
Support 'page_first_direct' layout for mambaPoolHost
hzh0425 Mar 15, 2026
6874520
Optimize code style
hzh0425 Mar 15, 2026
a03ae57
Optimize code style
hzh0425 Mar 15, 2026
2836195
Optimize code style
hzh0425 Mar 16, 2026
91f954f
Remove pp filter
hzh0425 Mar 16, 2026
520ca17
Optimize layerwise waiting logic
hzh0425 Mar 16, 2026
3cd247d
Support loading mamba state only
hzh0425 Mar 16, 2026
77163eb
Using enum to replace str
hzh0425 Mar 16, 2026
df5867c
support MambaPoolHost layout
huangtingwei9988 Mar 16, 2026
0f2d251
rollback format
hzh0425 Mar 16, 2026
83c6104
Optimize code style
hzh0425 Mar 16, 2026
b67edcb
Simplify Code in hicache_storage.py
huangtingwei9988 Mar 16, 2026
18d724b
fix evict num
ispobock Mar 16, 2026
3204f85
remove unused
ispobock Mar 16, 2026
abfc3d3
Simplify Code
huangtingwei9988 Mar 17, 2026
913626a
Merge remote-tracking branch 'origin/main' into hicache/hicache-refac…
hzh0425 Mar 17, 2026
6318d9c
Revert "Simplify Code"
hzh0425 Mar 17, 2026
571bce4
Add comment for io kernel
hzh0425 Mar 17, 2026
c825a08
Fix mixed chunk ci
hzh0425 Mar 17, 2026
0655c3c
Merge remote-tracking branch 'origin/main' into hicache/hicache-refac…
hzh0425 Mar 18, 2026
83b7663
Optimize log; Add CI
hzh0425 Mar 18, 2026
61f6670
support mooncake backend & dsa model
huangtingwei9988 Mar 22, 2026
e9e3c8d
fix bugs
huangtingwei9988 Mar 23, 2026
ffdf173
fix bugs while running dsa
huangtingwei9988 Mar 23, 2026
1d2134e
fix lint
huangtingwei9988 Mar 23, 2026
7dc50f9
merge main
huangtingwei9988 Mar 24, 2026
934874b
merge fix
huangtingwei9988 Mar 24, 2026
d957bc4
Merge branch 'main' into hicache/hicache-refactor4
huangtingwei9988 Mar 24, 2026
98048c5
fix
huangtingwei9988 Mar 27, 2026
aacc0e9
address comments
vladnosiv Mar 27, 2026
1f506f9
fix metrics
huangtingwei9988 Mar 30, 2026
5d08e79
Merge branch 'main' into hicache/hicache-refactor4
huangtingwei9988 Mar 31, 2026
9bacae6
Reduce modifications in the HiRadix cache.
huangtingwei9988 Mar 31, 2026
75b15b1
fix
huangtingwei9988 Mar 31, 2026
8dd2287
fix
huangtingwei9988 Mar 31, 2026
d0acd7e
Merge branch 'main' into hicache/hicache-refactor4
huangtingwei9988 Apr 3, 2026
94bfc89
Merge branch 'main' into hicache/hicache-refactor4
huangtingwei9988 Apr 7, 2026
1777830
Merge branch 'main' into hicache/hicache-refactor4
huangtingwei9988 Apr 8, 2026
095baf3
fix
huangtingwei9988 Apr 10, 2026
11d67ce
resolve conflict
huangtingwei9988 Apr 11, 2026
586b337
opt
huangtingwei9988 Apr 12, 2026
973110d
build_mamba_hybrid_stack
huangtingwei9988 Apr 12, 2026
ebf777f
Merge branch 'main' into hicache/hicache-refactor4
huangtingwei9988 Apr 12, 2026
fccef9e
fix ci
huangtingwei9988 Apr 13, 2026
b15552e
Merge branch 'hicache/hicache-refactor4' of github.com:antgroup/sglan…
huangtingwei9988 Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion python/sglang/srt/mem_cache/hicache_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ class PoolName(str, Enum):

KV = "kv"
MAMBA = "mamba"
INDEXER = "indexer"


class PoolHitPolicy(str, Enum):
"""Hit policy for batch_exists_v2 per-pool prefix matching.

ALL_PAGES : every page in [0, kv_hit) must exist (default).
ALL_PAGES : every page in [0, kv_hit) must exist (e.g. DSA).
TRAILING_PAGES : only the last N pages must exist (e.g. Mamba/SWA states).
"""

Expand Down
122 changes: 86 additions & 36 deletions python/sglang/srt/mem_cache/hiradix_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@
MatchPrefixParams,
MatchResult,
)
from sglang.srt.mem_cache.hicache_storage import (
PoolHitPolicy,
PoolName,
PoolTransfer,
)
from sglang.srt.mem_cache.hybrid_cache.hybrid_cache_controller import (
HybridCacheController,
)
from sglang.srt.mem_cache.hybrid_cache.hybrid_pool_assembler import (
build_nsa_hybrid_stack,
)
from sglang.srt.mem_cache.memory_pool import (
MHATokenToKVPool,
MLATokenToKVPool,
Expand All @@ -33,7 +44,6 @@
from sglang.srt.mem_cache.memory_pool_host import (
MHATokenToKVPoolHost,
MLATokenToKVPoolHost,
NSATokenToKVPoolHost,
)
from sglang.srt.mem_cache.radix_cache import (
RadixCache,
Expand Down Expand Up @@ -70,14 +80,8 @@ def __init__(self, params: CacheInitParams, server_args: ServerArgs):
allocator_type=server_args.hicache_storage_backend,
)
elif isinstance(self.kv_cache, NSATokenToKVPool):
self.token_to_kv_pool_host = NSATokenToKVPoolHost(
self.kv_cache,
server_args.hicache_ratio,
server_args.hicache_size,
self.page_size,
server_args.hicache_mem_layout,
allocator_type=server_args.hicache_storage_backend,
)
# Filled by build_nsa_hybrid_stack after storage extra_config is parsed.
self.token_to_kv_pool_host = None
elif isinstance(self.kv_cache, MLATokenToKVPool):
self.token_to_kv_pool_host = MLATokenToKVPoolHost(
self.kv_cache,
Expand All @@ -88,7 +92,9 @@ def __init__(self, params: CacheInitParams, server_args: ServerArgs):
allocator_type=server_args.hicache_storage_backend,
)
else:
raise ValueError(f"HiRadixCache only supports MHA and MLA yet")
raise ValueError(
"HiRadixCache only supports MHA, MLA, and NSA (DSA) models"
)

self.tp_group = params.tp_cache_group
self.tp_world_size = torch.distributed.get_world_size(group=self.tp_group)
Expand All @@ -112,22 +118,33 @@ def __init__(self, params: CacheInitParams, server_args: ServerArgs):
self.prefetch_stop_policy = server_args.hicache_storage_prefetch_policy

self.load_cache_event = threading.Event()
self.cache_controller = HiCacheController(
params.token_to_kv_pool_allocator,
self.token_to_kv_pool_host,
self.page_size,
self.tp_group,
load_cache_event=self.load_cache_event,
write_policy=server_args.hicache_write_policy,
io_backend=server_args.hicache_io_backend,
storage_backend=server_args.hicache_storage_backend,
prefetch_threshold=prefetch_threshold,
model_name=server_args.served_model_name,
storage_backend_extra_config=extra_config,
pp_rank=self.pp_rank,
pp_size=self.pp_size,
enable_storage_metrics=self.enable_storage_metrics,
)
if isinstance(self.kv_cache, NSATokenToKVPool):
build_nsa_hybrid_stack(
self,
params,
server_args,
extra_config=extra_config,
prefetch_threshold=prefetch_threshold,
enable_storage_metrics=self.enable_storage_metrics,
load_cache_event=self.load_cache_event,
)
else:
Comment thread
huangtingwei9988 marked this conversation as resolved.
self.cache_controller = HiCacheController(
params.token_to_kv_pool_allocator,
self.token_to_kv_pool_host,
self.page_size,
self.tp_group,
load_cache_event=self.load_cache_event,
write_policy=server_args.hicache_write_policy,
io_backend=server_args.hicache_io_backend,
storage_backend=server_args.hicache_storage_backend,
prefetch_threshold=prefetch_threshold,
model_name=server_args.served_model_name,
storage_backend_extra_config=extra_config,
pp_rank=self.pp_rank,
pp_size=self.pp_size,
enable_storage_metrics=self.enable_storage_metrics,
)
self._apply_storage_runtime_config(
storage_backend=server_args.hicache_storage_backend,
prefetch_threshold=prefetch_threshold,
Expand Down Expand Up @@ -315,12 +332,21 @@ def attach_storage_backend(
)

try:
self.cache_controller.attach_storage_backend(
storage_backend=storage_backend,
prefetch_threshold=prefetch_threshold,
model_name=served_model_name,
storage_backend_extra_config=extra_config,
)
if isinstance(self.cache_controller, HybridCacheController):
self.cache_controller.attach_storage_backend(
storage_backend=storage_backend,
prefetch_threshold=prefetch_threshold,
model_name=served_model_name,
storage_backend_extra_config=extra_config,
host_pools=self.cache_controller.mem_pool_host.entries,
)
else:
self.cache_controller.attach_storage_backend(
storage_backend=storage_backend,
prefetch_threshold=prefetch_threshold,
model_name=served_model_name,
storage_backend_extra_config=extra_config,
)
except Exception as e:
logger.exception(
f"Failed to attach storage backend '{storage_backend}': {e}"
Expand Down Expand Up @@ -585,6 +611,18 @@ def get_height(self, node: TreeNode):
height += 1
return height

def _get_extra_pools(self) -> dict:
if not isinstance(self.cache_controller, HybridCacheController):
return {}
if isinstance(self.kv_cache, NSATokenToKVPool):
pool = PoolTransfer(
name=PoolName.INDEXER,
hit_policy=PoolHitPolicy.ALL_PAGES,
)
return {"extra_pools": [pool]}
else:
return {}

def clear_storage_backend(self) -> bool:
if self.enable_storage:
try:
Expand Down Expand Up @@ -615,12 +653,14 @@ def write_backup(self, node: TreeNode, write_back=False):
host_indices = self.cache_controller.write(
device_indices=node.value,
node_id=node.id,
**self._get_extra_pools(),
)
if host_indices is None:
self.evict_host(len(node.value))
host_indices = self.cache_controller.write(
device_indices=node.value,
node_id=node.id,
**self._get_extra_pools(),
)
if host_indices is not None:
node.host_value = host_indices.clone()
Expand All @@ -642,7 +682,8 @@ def write_backup_storage(self, node: TreeNode):
)

operation_id = self.cache_controller.write_storage(
node.host_value, node.key, node.hash_value, prefix_keys
node.host_value, node.key, node.hash_value, prefix_keys,
**self._get_extra_pools(),
)
self.ongoing_backup[operation_id] = node
node.protect_host()
Expand Down Expand Up @@ -913,12 +954,16 @@ def load_back(
return None

device_indices = self.cache_controller.load(
host_indices=host_indices, node_id=last_hit_node.id
host_indices=host_indices,
node_id=last_hit_node.id,
**self._get_extra_pools(),
)
if device_indices is None:
self.evict(EvictParams(num_tokens=len(host_indices)))
device_indices = self.cache_controller.load(
host_indices=host_indices, node_id=last_hit_node.id
host_indices=host_indices,
node_id=last_hit_node.id,
**self._get_extra_pools(),
)
self.dec_lock_ref(ancester_node)
if device_indices is None:
Expand Down Expand Up @@ -1219,7 +1264,12 @@ def prefetch_from_storage(
# no sufficient host memory for prefetch
return
operation = self.cache_controller.prefetch(
req_id, host_indices, new_input_tokens, last_hash, prefix_keys
req_id,
host_indices,
new_input_tokens,
last_hash,
prefix_keys,
**self._get_extra_pools(),
)
self.ongoing_prefetch[req_id] = (
last_host_node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ def __init__(
pp_rank: int = 0,
pp_size: int = 1,
transfer_layer_num: Optional[int] = None,
enable_storage_metrics: bool = False,
):
startup_storage_backend = storage_backend
super().__init__(
Expand All @@ -179,6 +180,7 @@ def __init__(
storage_backend_extra_config=storage_backend_extra_config,
pp_rank=pp_rank,
pp_size=pp_size,
enable_storage_metrics=enable_storage_metrics,
)
# Override layer_num: hybrid models transfer all layers (For example, Linear Model (KV + Mamba)),
# not just the full attention layers reported by full_kv_pool.
Expand Down Expand Up @@ -230,7 +232,10 @@ def write(
if host_indices is None:
return None
pool_transfers = self._resolve_pool_transfers_allocation(
extra_pools, alloc_host=True
extra_pools,
alloc_host=True,
kv_device_indices=device_indices,
kv_host_indices=host_indices,
)
if pool_transfers is None and extra_pools:
self.mem_pool_host.free(host_indices)
Expand Down Expand Up @@ -289,7 +294,10 @@ def load(
device_indices = torch.empty((0,), dtype=torch.int64, device=self.device)

pool_transfers = self._resolve_pool_transfers_allocation(
extra_pools, alloc_host=False
extra_pools,
alloc_host=False,
kv_device_indices=device_indices,
kv_host_indices=host_indices,
)
if pool_transfers is None and extra_pools:
if need_load_kv:
Expand Down Expand Up @@ -415,6 +423,7 @@ def _storage_hit_query(self, operation) -> tuple[list[str], int]:
def _page_transfer(self, operation):
# Transfer extra pools
if operation.pool_transfers and not operation.is_terminated():
self._resolve_shared_pool_transfers(operation)
results = self.storage_backend.batch_get_v2(operation.pool_transfers)
operation.pool_storage_result.update_extra_pool_hit_pages(results)

Expand All @@ -424,12 +433,20 @@ def _page_transfer(self, operation):
def _page_backup(self, operation):
# Backup extra pools
if operation.pool_transfers:
self._resolve_shared_pool_transfers(operation)
results = self.storage_backend.batch_set_v2(operation.pool_transfers)
operation.pool_storage_result.update_extra_pool_hit_pages(results)

# Backup kv pools
super()._page_backup(operation)
Comment on lines 433 to 441

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like atomic. But HiCacheController._page_backup does not seem to be atomic, will it cause a mismatch bug in the future?


def _resolve_shared_pool_transfers(self, operation):
for transfer in operation.pool_transfers:
entry = self.mem_pool_host.entry_map.get(transfer.name)
if entry.share_indices_with_anchor:
transfer.keys = operation.hash_value
transfer.host_indices = operation.host_indices

def _sync_trailing_keys(
self,
pool_transfers: list[PoolTransfer],
Expand All @@ -454,6 +471,8 @@ def _resolve_pool_transfers_allocation(
self,
extra_pools: Optional[list[PoolTransfer]],
alloc_host: bool,
kv_device_indices: Optional[torch.Tensor] = None,
kv_host_indices: Optional[torch.Tensor] = None,
) -> Optional[list[PoolTransfer]]:
"""Auto-alloc host or device indices for PoolTransfers where they are None."""
if not extra_pools:
Expand All @@ -463,6 +482,10 @@ def _resolve_pool_transfers_allocation(
entry = self.mem_pool_host.entry_map.get(pool.name)
if entry is None:
continue
if entry.share_indices_with_anchor:
pool.device_indices = kv_device_indices
pool.host_indices = kv_host_indices
continue
if alloc_host:
if pool.host_indices is not None or pool.device_indices is None:
continue
Expand Down
Loading
Loading