From 8fd6e9e01789bd458dd420bab8284f92af305698 Mon Sep 17 00:00:00 2001 From: ziang663 <119752791+ziang663@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:49:23 +0800 Subject: [PATCH] Fix fixed-size HiCache capacity under PP (#29258) --- .../sglang/srt/mem_cache/memory_pool_host.py | 5 +- python/sglang/srt/mem_cache/pool_host/base.py | 46 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/python/sglang/srt/mem_cache/memory_pool_host.py b/python/sglang/srt/mem_cache/memory_pool_host.py index cbbd07b3b..5f0618cc5 100644 --- a/python/sglang/srt/mem_cache/memory_pool_host.py +++ b/python/sglang/srt/mem_cache/memory_pool_host.py @@ -73,6 +73,7 @@ logger = logging.getLogger(__name__) from sglang.srt.mem_cache.pool_host import HostKVCache from sglang.srt.mem_cache.pool_host.base import ( HICACHE_HOST_MEMORY_RESERVE_BYTES, + sync_fixed_hicache_size, synchronized, ) from sglang.srt.mem_cache.pool_host.common import ( @@ -1435,7 +1436,9 @@ class MambaPoolHost(HostKVCache): self.size_per_token = self.get_size_per_token() if host_size > 0: - self.size = int(host_size * 1e9 // self.size_per_token) + self.size = sync_fixed_hicache_size( + int(host_size * 1e9 // self.size_per_token), host_size + ) else: self.size = int(device_pool.size * host_to_device_ratio) diff --git a/python/sglang/srt/mem_cache/pool_host/base.py b/python/sglang/srt/mem_cache/pool_host/base.py index 0329fc7ca..0ca962225 100644 --- a/python/sglang/srt/mem_cache/pool_host/base.py +++ b/python/sglang/srt/mem_cache/pool_host/base.py @@ -25,6 +25,48 @@ _is_hip = is_hip() HICACHE_HOST_MEMORY_RESERVE_BYTES: int = 10 * (1024**3) +def sync_fixed_hicache_size(size: int, host_size: int) -> int: + """Sync fixed-size HiCache token capacity across PP ranks. + + A fixed --hicache-size is specified in GB, but each PP stage may have a + different bytes/token because it owns different layers. Use the global + minimum token capacity within the PP group so all stages expose the same + host-cache capacity. + Ratio-based sizing already derives from the synced device pool size. + """ + if host_size <= 0 or not torch.distributed.is_available(): + return size + + if not torch.distributed.is_initialized(): + return size + + try: + from sglang.srt.distributed.parallel_state import get_pp_group + + pp_group = get_pp_group() + except AssertionError: + return size + + if pp_group.world_size <= 1: + return size + + tensor = torch.tensor(size, dtype=torch.int64) + torch.distributed.all_reduce( + tensor, + op=torch.distributed.ReduceOp.MIN, + group=pp_group.cpu_group, + ) + synced_size = int(tensor.item()) + + if synced_size != size: + logger.info( + "Sync fixed-size HiCache host token capacity from %d to %d.", + size, + synced_size, + ) + return synced_size + + def synchronized(func): @wraps(func) def wrapper(self, *args, **kwargs): @@ -58,7 +100,9 @@ class HostKVCache(abc.ABC): self.dtype = device_pool.store_dtype self.size_per_token = self.get_size_per_token() if host_size > 0: - self.size = int(host_size * 1e9 // self.size_per_token) + self.size = sync_fixed_hicache_size( + int(host_size * 1e9 // self.size_per_token), host_size + ) else: self.size = int(device_pool.size * host_to_device_ratio) # Align up the host memory pool size to the page size