Compact skipped NSA index-cache state safely

Index skip reduces the number of target layers that own NSA index state,
but PD transfer and HiCache still assumed dense full-layer state buffers.
This change carries explicit state layer IDs through prefill/decode
registration, compacts device and host index buffers to active layers,
and maps logical layer IDs to compact slots on transfer paths.

The PD side fails fast when prefill/decode disagree on NSA state layer
identity instead of silently truncating or copying mismatched buffers.
Host direct tests now use the same CPU-index descriptor contract required
by the TAI cudaMemcpyBatchAsync path, and host registered memory is
unregistered on tensor finalization to avoid stale cudaHostRegister state
across CUDA tests.

Constraint: CP shared-KV with index_topk skip must keep target/draft state identity explicit before compacting buffers
Constraint: Direct HiCache TAI transfer rejects CUDA indices to avoid hidden D2H copies on the control path
Rejected: Keep full-layer L1/L2 index buffers | wastes the memory/bandwidth that index skip is meant to save
Rejected: Infer state buffer order by count only | can silently corrupt cache when active layer sets differ
Confidence: high
Scope-risk: moderate
Directive: Do not compact or reorder NSA state buffers without carrying logical layer IDs through PD registration and validating both sides
Tested: Remote container py_compile for touched runtime files
Tested: Remote container pytest: test_nsa_pool_host_unit.py, test_model_runner_kv_cache_mixin.py, test_cp_shared_kv_transfer_mapping.py, test_pd_state_layer_ids.py, test_cp_per_layer_transfer.py, test_cp_shared_kv_runtime.py -> 200 passed, 2 subtests passed
Not-tested: Full ETE GSM8K/replay after compacted P3-P6 changes
Co-authored-by: OmX <omx@oh-my-codex.dev>
This commit is contained in:
laoyao0822
2026-06-10 05:37:06 +08:00
co-authored by OmX
parent d21952b903
commit 1ebde44e59
14 changed files with 611 additions and 16 deletions
@@ -0,0 +1,41 @@
from types import SimpleNamespace
import torch
from sglang.srt.mem_cache.memory_pool import NSATokenToKVPool
from sglang.srt.model_executor.model_runner_kv_cache_mixin import (
ModelRunnerKVCacheMixin,
)
from sglang.test.ci.ci_register import register_cpu_ci
register_cpu_ci(est_time=1, suite="stage-a-test-cpu")
def test_nsa_cell_size_uses_active_index_layer_count():
hf_config = SimpleNamespace(
architectures=["DeepseekV3ForCausalLM"],
index_topk=2048,
index_topk_freq=4,
index_head_dim=128,
)
runner = SimpleNamespace(
use_mla_backend=True,
kv_cache_dtype=torch.bfloat16,
model_config=SimpleNamespace(
hf_config=hf_config,
kv_lora_rank=128,
qk_rope_head_dim=32,
),
start_layer=0,
end_layer=12,
is_draft_worker=False,
)
cell_size = ModelRunnerKVCacheMixin.get_cell_size_per_token(runner, num_layers=12)
indexer_size_per_token = (
hf_config.index_head_dim
+ hf_config.index_head_dim // NSATokenToKVPool.quant_block_size * 4
)
expected = (128 + 32) * 12 * 2 + indexer_size_per_token * 4
assert cell_size == expected