CP HiCache metrics: fix tp_rank AttributeError under CP + label by cp_rank

_cp_maybe_collect_metrics read cache_controller.tp_rank/dp_rank, but those were
resolved only inside _generate_storage_config -- which never runs under CP
shared-L2 (storage backends are forbidden there), so the first metrics emit
crashed the prefill scheduler with:
  AttributeError: 'HiCacheController' object has no attribute 'tp_rank'

- cache_controller.py: resolve tp_rank/tp_size/dp_rank once in __init__ (always
  available, not storage-gated); drop the now-redundant derivation in
  _generate_storage_config.
- hiradix_cache.py: label the CP HiCache metrics by cp_rank/cp_size -- the
  identity that actually distinguishes the per-rank L2 lanes / L3 slabs these
  metrics measure -- resolved from the CP layout (_cp_hicache_cp_*), alongside
  the now-reliable tp/dp/pp coordinates.
- metrics_collector.py: docstring -> per-rank via cp_rank.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-23 16:06:51 +00:00
parent dbc5ebbaa0
commit 47c24fecf2
3 changed files with 23 additions and 10 deletions

View File

@@ -427,6 +427,19 @@ class HiCacheController:
self.pp_size = pp_size
self.enable_storage_metrics = enable_storage_metrics
# Resolve this rank's parallel identity once, unconditionally -- not only
# when a storage backend is configured. CP shared-L2 attaches no storage
# backend, yet per-rank consumers (e.g. the CP HiCache metrics collector)
# still need tp_rank/tp_size/dp_rank.
if is_dp_attention_enabled():
self.tp_rank = get_attention_tp_rank()
self.tp_size = get_attention_tp_size()
self.dp_rank = get_attention_dp_rank()
else:
self.tp_rank = get_tensor_model_parallel_rank()
self.tp_size = get_tensor_model_parallel_world_size()
self.dp_rank = 0
# Default storage page IO functions (may be overridden by attach).
self.page_get_func = self._generic_page_get
self.page_set_func = self._generic_page_set
@@ -788,15 +801,8 @@ class HiCacheController:
if storage_backend_extra_config is None:
storage_backend_extra_config = {}
if is_dp_attention_enabled():
self.tp_rank = get_attention_tp_rank()
self.tp_size = get_attention_tp_size()
self.dp_rank = get_attention_dp_rank()
else:
self.tp_rank = get_tensor_model_parallel_rank()
self.tp_size = get_tensor_model_parallel_world_size()
self.dp_rank = 0
# tp_rank/tp_size/dp_rank are resolved once in __init__ (always available,
# including under CP shared-L2 where no storage backend is attached).
# Currently, NPUMLATokenToKVPool is the subclass of MLATokenToKVPool.
is_mla_backend = isinstance(self.mem_pool_device, MLATokenToKVPool)
# Least Common Multiple among heterogeneous tp size

View File

@@ -3910,7 +3910,13 @@ class HiRadixCache(RadixCache):
from sglang.srt.observability.metrics_collector import CpHiCacheMetricsCollector
cc = self.cache_controller
# cp_rank/cp_size are the identity that actually distinguishes the
# per-rank L2 lanes / L3 slabs (what these metrics measure); tp/dp/pp
# give the full parallel coordinate. cp_* come from the CP layout
# (_cp_hicache_cp_*), the rest from the controller (always set).
labels = {
"cp_rank": self._cp_hicache_cp_rank(),
"cp_size": self._cp_hicache_cp_size(),
"tp_rank": cc.tp_rank, "dp_rank": cc.dp_rank,
"pp_rank": cc.pp_rank, "pp_size": cc.pp_size,
}

View File

@@ -1464,7 +1464,8 @@ class CpHiCacheMetricsCollector:
Gauges for instantaneous state (occupancy, inflight, queue depths); Counters for cumulative work
(spill/reload pages, GC reclaims, write errors, L2 commit/evict). Fed periodically off the allocator's
occupancy_by_payload()/stats() + CpL3Store.stats(). Per-rank via the tp_rank label. The mainline
occupancy_by_payload()/stats() + CpL3Store.stats(). Per-rank via the cp_rank label (each CP rank owns
its own L2 lane + L3 slabs); tp/dp/pp labels give the full parallel coordinate. The mainline
StorageMetricsCollector is dead under CP (enable_storage=False), so this is the only L2/L3 visibility.
"""