feat: map CP HiCache loads to owned physical slots

This commit is contained in:
2026-05-08 02:16:04 +08:00
parent 08d518c2ed
commit 94df7a6909
2 changed files with 76 additions and 0 deletions

View File

@@ -790,6 +790,41 @@ class HiCacheController:
)
return device_indices
def load_cp(self, nodes_to_load, node_id: int = -1) -> Optional[torch.Tensor]:
logical_len = sum(node.host_len for node in nodes_to_load)
device_indices = self.mem_pool_device_allocator.alloc(logical_len)
if device_indices is None:
return None
host_chunks = []
physical_chunks = []
offset = 0
for node in nodes_to_load:
node_device_indices = device_indices[offset : offset + node.host_len]
offset += node.host_len
owned_positions = node.cp_hicache.owned_positions.to(device_indices.device)
if owned_positions.numel() == 0:
continue
selected_logical_locs = node_device_indices[owned_positions]
physical_chunks.append(
self.cp_shared_kv_layout.logical_locs_to_physical(selected_logical_locs)
)
host_chunks.append(node.cp_hicache.host_indices.to(device_indices.device))
if not host_chunks:
self._append_completed_load_ack(node_id)
return device_indices
self.load_queue.append(
CacheOperation(
torch.cat(host_chunks),
torch.cat(physical_chunks),
node_id,
)
)
return device_indices
def move_indices(self, op: CacheOperation):
host_indices, device_indices = op.host_indices, op.device_indices
# move indices to GPU if using kernels, to host if using direct indexing

View File

@@ -29,6 +29,8 @@ if "sgl_kernel.kvcacheio" not in sys.modules:
from sglang.srt.managers.cache_controller import HiCacheController
from sglang.srt.mem_cache.cp_shared_kv_layout import CpSharedKVLayout
from sglang.srt.mem_cache.hiradix_cache import CpHiCacheNodeMetadata
from sglang.srt.mem_cache.radix_cache import TreeNode
from sglang.test.ci.ci_register import register_cpu_ci
from sglang.test.test_utils import CustomTestCase
@@ -206,5 +208,44 @@ class TestHiCacheControllerCPWrite(CustomTestCase):
self.assertEqual(result.required_host_slots, 4)
class TestHiCacheControllerCPLoad(TestHiCacheControllerCPWrite):
def test_cp_load_allocates_full_logical_locs_and_transfers_owned_physical_locs(self):
host_pool = FakeHostPool(torch.tensor([100, 101, 102, 103], dtype=torch.int64))
allocator = FakeAllocator(alloc_result=torch.arange(64, 80, dtype=torch.int64))
controller = self.make_controller(host_pool, allocator=allocator, cp_rank=1)
node = TreeNode()
node.host_len = 16
node.cp_hicache = CpHiCacheNodeMetadata(
logical_len=16,
owned_positions=torch.tensor([4, 5, 6, 7], dtype=torch.int64),
host_indices=torch.tensor([100, 101, 102, 103], dtype=torch.int64),
)
device_indices = controller.load_cp([node], node_id=11)
controller.start_loading()
self.assertEqual(device_indices.tolist(), list(range(64, 80)))
self.assertEqual(allocator.alloc_calls, [16])
self.assertEqual(host_pool.loads[0][1].tolist(), [20, 21, 22, 23])
def test_cp_load_zero_owned_returns_full_logical_locs_and_noop_ack(self):
host_pool = FakeHostPool(torch.tensor([], dtype=torch.int64))
allocator = FakeAllocator(alloc_result=torch.arange(64, 68, dtype=torch.int64))
controller = self.make_controller(host_pool, allocator=allocator, cp_rank=3)
node = TreeNode()
node.host_len = 4
node.cp_hicache = CpHiCacheNodeMetadata(
logical_len=4,
owned_positions=torch.empty((0,), dtype=torch.int64),
host_indices=torch.empty((0,), dtype=torch.int64),
)
device_indices = controller.load_cp([node], node_id=12)
self.assertEqual(device_indices.tolist(), [64, 65, 66, 67])
self.assertEqual(host_pool.loads, [])
self.assertEqual(len(controller.ack_load_queue), 1)
if __name__ == "__main__":
main()