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:
@@ -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
|
||||
Reference in New Issue
Block a user