CP HiCache trace: key KV-value hashes by host-slot fingerprint + zero-KV flag

The first KV-value pass couldn't compare (reload hashes keyed by the merged
load op's node id, backup hashes per node -> no join). Fix: emit the host-slot
range fingerprint at both backup and reload (host slots are the stable identity
across node id / splits / merge), and the analyzer matches on
(rank, layer, host_fp). Also add a nonzero-byte count (nz): real KV is never
all-zero, so an nz==0 slice is uninitialized/zero KV -- a direct, self-contained
corruption flag that needs no cross-stage matching. This targets the observed
'0|0|0...' garbage on large-cached reloads (store never filled the backed-up
pages, or the round-trip lost them). Gated at SGLANG_CP_HICACHE_KV_TRACE=2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 02:29:00 +00:00
parent 5ed20f493a
commit e3cfba8441
2 changed files with 25 additions and 3 deletions

View File

@@ -23,7 +23,7 @@ from typing import TYPE_CHECKING, Dict, List, NamedTuple, Optional, Set
import torch
from sglang.srt.environ import envs
from sglang.srt.mem_cache.cp_hicache_trace import cptrace, khash, rng, trace_enabled
from sglang.srt.mem_cache.cp_hicache_trace import cptrace, khash, knz, rng, trace_enabled
from sglang.srt.mem_cache.hicache_storage import HiCacheStorageConfig
if TYPE_CHECKING:
@@ -1497,12 +1497,15 @@ class HiCacheController:
if trace_enabled(2):
for state in target_transfer_states:
kv = self.mem_pool_device.get_key_buffer(layer_id)
rows = kv[state.physical_device_indices]
cptrace(
2,
"backup_kv_hash",
node_id=state.reservation.node_id,
layer=layer_id,
kvhash=khash(kv[state.physical_device_indices]),
host=rng(state.host_indices),
kvhash=khash(rows),
nz=knz(rows),
nrows=int(state.physical_device_indices.numel()),
)
@@ -1975,12 +1978,15 @@ class HiCacheController:
# op's nodes (single node in the c=1 rehit pass).
if trace_enabled(2):
kv = self.mem_pool_device.get_key_buffer(i)
rows = kv[device_indices]
cptrace(
2,
"reload_kv_hash",
node_ids=node_ids,
layer=i,
kvhash=khash(kv[device_indices]),
host=rng(host_indices),
kvhash=khash(rows),
nz=knz(rows),
nrows=int(device_indices.numel()),
)
producer_event.complete(i)

View File

@@ -79,6 +79,22 @@ def khash(t) -> int:
return -1
def knz(t) -> int:
"""Count of nonzero bytes in a KV slice. Real KV is never all-zero, so a
reload (or backup) slice with nz==0 = uninitialized/zero KV -- a direct,
self-contained corruption flag (no cross-stage matching needed). Catches the
observed '0|0|0...' garbage where the store never filled the backed-up pages."""
try:
import torch
if t is None or t.numel() == 0:
return 0
b = t.detach().contiguous().view(torch.uint8).reshape(-1)
return int((b != 0).sum().item())
except Exception:
return -1
def cptrace(level: int, tag: str, **fields) -> None:
"""Emit one ``[CPTRACE <tag>] k=v ...`` line if the gate >= level.