fix: synchronize CP HiCache host eviction semantics

This commit is contained in:
2026-05-08 03:20:47 +08:00
parent eecc8e21ec
commit 8844c303e2
4 changed files with 144 additions and 6 deletions

View File

@@ -435,6 +435,11 @@ class HiCacheController:
Requirement: no in-flight requests. This call is expected to run on the scheduler
thread (control path), not concurrently with prefetch/backup.
"""
if self.uses_cp_hicache:
raise RuntimeError(
"CP shared KV HiCache does not support attaching a storage backend at runtime."
)
if self.enable_storage:
raise RuntimeError("Storage backend already attached.")

View File

@@ -331,6 +331,12 @@ class HiRadixCache(RadixCache):
prefetch/backup paths. Caller must ensure there are no running/queued
requests to avoid races.
"""
if self._uses_cp_hicache:
return (
False,
"CP shared KV HiCache does not support attaching a storage backend at runtime.",
)
# Validate inputs first (no side effects).
if hicache_storage_prefetch_policy is not None:
allowed = ["best_effort", "wait_complete", "timeout"]
@@ -726,8 +732,14 @@ class HiRadixCache(RadixCache):
device_indices=node.value,
node_id=node.id,
)
required_host_slots = 0
if getattr(result, "metadata", None) is None:
required_host_slots = result.required_host_slots
self._evict_host_for_physical_slots(
required_host_slots,
synchronize_across_ranks=getattr(self, "tp_world_size", 1) > 1,
)
if getattr(result, "metadata", None) is None:
self._evict_host_for_physical_slots(result.required_host_slots)
result = self.cache_controller.write(
device_indices=node.value,
node_id=node.id,
@@ -741,7 +753,7 @@ class HiRadixCache(RadixCache):
self.ongoing_write_through[node.id] = node
if not write_back:
self.inc_node_lock_ref(node)
return len(node.cp_hicache.host_indices)
return len(node.value)
host_indices = self.cache_controller.write(
device_indices=node.value,
@@ -1084,8 +1096,13 @@ class HiRadixCache(RadixCache):
self._update_host_leaf_status(parent)
return parent
def _evict_host_for_physical_slots(self, required_host_slots: int) -> int:
if required_host_slots <= 0:
def _evict_host_for_physical_slots(
self, required_host_slots: int, synchronize_across_ranks: bool = False
) -> int:
synchronize_across_ranks = (
synchronize_across_ranks and getattr(self, "tp_world_size", 1) > 1
)
if required_host_slots <= 0 and not synchronize_across_ranks:
return 0
leaves = list(self.evictable_host_leaves)
@@ -1095,7 +1112,18 @@ class HiRadixCache(RadixCache):
heapq.heapify(eviction_heap)
num_evicted = 0
while num_evicted < required_host_slots and len(eviction_heap):
def all_ranks_done() -> bool:
local_done = int(num_evicted >= required_host_slots)
if not synchronize_across_ranks:
return bool(local_done)
done = torch.tensor(local_done, dtype=torch.int, device="cpu")
torch.distributed.all_reduce(
done, op=torch.distributed.ReduceOp.MIN, group=self.tp_group
)
return bool(done.item())
while len(eviction_heap) and not all_ranks_done():
_priority, x = heapq.heappop(eviction_heap)
if x == self.root_node:
break