Keep CP shared-KV prefetch warnings actionable

Expected no-prefetch paths were polluting production logs: no cache prefix, tiny/first-layer windows, and FP8 RAGGED top-k were being reported as fallback warnings. The prefetch contract now treats zero-prefix and first-layer misses as normal skips, while preserving warnings for non-zero misaligned prefixes and real consume misses after the first layer. The same change keeps RAGGED cache-hit prefetch eligible and records the CE/IPM prefetch contract in the plan doc.

Constraint: FP8 sparse prefill uses RAGGED top-k, but CP shared-KV prefix materialization is still page-slot based

Constraint: Layer 0 has no previous attention-window hook that can have prefetched the layer

Rejected: Warn whenever a prefetcher is absent | no-cache and too-short requests are expected synchronous paths and make logs unusable

Confidence: high

Scope-risk: moderate

Directive: Keep CP_SHARED_KV_FALLBACK warnings for unexpected contract failures only; use debug logs for expected skip paths

Tested: Local py_compile for cp_shared_kv_prefetch.py, nsa_indexer.py, nsa_backend.py

Tested: Remote cjy-glm5-new targeted regression: 3 passed, 21 warnings

Tested: Remote cjy-glm5-new full test_cp_shared_kv_runtime.py: 156 passed, 21 warnings, 2 subtests passed

Not-tested: New ETE run after prefill restart to confirm log volume reduction in production traffic
(cherry picked from commit e08e321e5929fdbb30102ec0b19c6ff0ecac7e7e)
This commit is contained in:
laoyao0822
2026-06-12 08:11:51 +08:00
parent da8bd7f813
commit 4d5c7f32d6
6 changed files with 1509 additions and 582 deletions

View File

@@ -430,15 +430,37 @@ L2->L1 load finished on owner rank
### P0. 文档与现状锁定
**文件:**
**状态2026-06-12已完成。**
- 新增:`docs/advanced_features/nsa_prefill_cp_shared_kv_bs_gt1_l1_prefetch_zero_sm_plan_zh.md`
完成内容:
**验收:**
1. L1 prefetch create 路径不再以 `get_attention_cp_group().pynccl_comm` 作为启用条件。
prefetch 的新合同是 IPC/CE materialize不再依赖 CP collective communicator。
2. `cp_shared_kv_prefetch.py``nsa_backend.py` 中 prefetch hot path 已清理旧
`launch_pending_reduce` / `prefix_reduce` / `reduce_enqueue` / `collective_disallowed`
命名,统一改成 `finalize_pending_materialize` / `prefix_materialize` /
`materialize_enqueue` / `materialize_event_missing`
3. prefetch 文件中不再 import/use `_all_reduce_materialized_buffer_*`
`get_attention_cp_group`。如果 IPC/CE materialize 不可用CP>1 下应显式
fail-fast而不是 silent fallback 到 all-reduce。
4. 测试已覆盖:`maybe_create()` 不依赖 pyncclattention-window 不再触发
prefix reduce 旧路径tiny extend skip 测试也不再 mock pynccl。
- 文档明确 L2->L1 `+2 layer`、L1 prefetch `+1 layer`
- 文档明确当前 prefetcher 的 bs=1 假设和风险。
- 文档明确 0SM 与现有 SM IPC kernel 的区别。
验收证据:
```text
远端 cjy-glm5-new:
PYTHONPATH=python python -m pytest -q test/registered/unit/mem_cache/test_cp_shared_kv_runtime.py
150 passed, 21 warnings, 2 subtests passed
```
静态检查:
```text
rg "pynccl|launch_pending_reduce|prefix_reduce|collective_disallowed|reduce_enqueue|get_attention_cp_group|_all_reduce" \
cp_shared_kv_prefetch.py nsa_backend.py
# no matches
```
### P1. 补 bs>1 prefetch plan 单测
@@ -508,17 +530,17 @@ PYTHONPATH=python python -m pytest -q \
- `current_slot_spans`
- `prefix_page_count`
- `current_page_count`
3. `start_next_layer_prefix()` 只 materialize/reduce `prefix_slot_spans`
3. `start_next_layer_prefix()` 只 materialize `prefix_slot_spans`
不再把 batch flattened page table 当成 `[0:prefix_pages)`
4. `consume_prefix_with_current()` reduce `current_slot_spans`,避免把
row gap / 其他 request prefix 一起 reduce。
4. `consume_prefix_with_current()`处理 `current_slot_spans`,避免把
row gap / 其他 request prefix 一起纳入 suffix compose。
5. `consume()` 的 legacy full-materialize suffix 路径也改为使用
`current_slot_spans`,避免 bs>1 bounding suffix。
当前限制:
- MLA prefix spans 仍走现有 materialize + async all-reduce baseline还没有
接入 spans-list TAI IPC 或 0SM CE
- MLA prefix spans 已不再走 async all-reduce fallback当前要求 IPC/CE materialize
生成 completion eventCP>1 下不可用则 fail-fast
- index prefetcher 仍未改造,继续由 P3 处理。
**文件:**
@@ -531,13 +553,13 @@ PYTHONPATH=python python -m pytest -q \
1. `CpSharedKVMlaPrefetcher.maybe_create()` 不再因为 `batch_size != 1` 直接 skip。
2. create 阶段构造 `prefix_slot_spans/current_slot_spans`
3. `start_next_layer_prefix()` 只 materialize prefix spans。
4. `consume_prefix_with_current()` fill/reduce current spans。
4. `consume_prefix_with_current()`处理 current spans。
5. `consume()` 如果仍存在 legacy suffix path必须使用 `current_slot_spans`
或 fail-fast不能回到错误 bounding suffix。
**第一版允许:**
- prefix 使用现有 local materialize + all-reduce 或 SM IPC baseline。
- prefix 使用现有 SM IPC 或 CE materialize baseline。
**第一版不允许:**
@@ -567,8 +589,8 @@ RED 证据:旧代码在 `batch_size=2` 时以
- `current_slot_spans`
- `prefix_page_count`
- `current_page_count`
3. `start_next_layer_prefix()` 只 materialize/reduce index prefix spans。
4. `consume_prefix_with_current()` fill/reduce index current spans。
3. `start_next_layer_prefix()` 只 materialize index prefix spans。
4. `consume_prefix_with_current()`处理 index current spans。
5. `consume()` 的 legacy suffix 路径也改为 `current_slot_spans`
不再使用 batch bounding suffix。
@@ -581,8 +603,8 @@ test_cp_shared_kv_runtime.py
当前限制:
- index prefetch 仍使用现有 materialize + async all-reduce baseline
spans-list TAI IPC / 0SM CE 留给后续 P5/P6
- index prefetch 已不再走 async all-reduce fallback当前要求 IPC/CE materialize
生成 completion eventCP>1 下不可用则 fail-fast
- active index layer / index skip 的 runtime hook 当前沿用已有
`nsa_backend.py` 调用点;本阶段没有修改 skip 参数语义。
@@ -637,11 +659,11 @@ test_cp_shared_kv_runtime.py
- `MLATokenToKVPool.get_key_buffer_for_prefetch(layer_id, stream)`
- `MLATokenToKVPool.get_index_k_with_scale_buffer_for_prefetch(layer_id, stream)`
- 发现并修正一个 ordering 疏漏P1-P3 后 prefix materialize 仍在 current stream 上执行,但 `start_next_layer_prefix()` 把 L2->L1 readiness wait 绑定到了 prefetch stream。这样只能保护后续 reduce不能保护实际读取 L1 raw pages 的 materialize。
- 修正策略MLA/index 的 `start_next_layer_prefix()` 先取得 `current_stream`,把它传给 prefetch-safe getter使 L2->L1 ready event 挂到实际读取 KV/index page 的 stream随后仍保持 `prefetch_stream.wait_stream(current_stream)`reduce 在 prefetch stream 上异步提交
- 修正策略MLA/index 的 `start_next_layer_prefix()` 先取得 `current_stream`,把它传给 prefetch-safe getter使 L2->L1 ready event 挂到实际读取 KV/index page 的 stream随后仍保持 stream ordering由 IPC/CE materialize event 表示 prefix ready
- 没有新增 collective没有把 forward stream 改成 CPU 同步等待。
- 单测锁住:
- `test_mla_prefetch_waits_l2_l1_on_materialize_stream_and_reduces_on_prefetch_stream`
- `test_index_prefetch_waits_l2_l1_on_materialize_stream_and_reduces_on_prefetch_stream`
- `test_mla_prefetch_waits_l2_l1_on_materialize_stream`
- `test_index_prefetch_waits_l2_l1_on_materialize_stream`
### P5. TAI SM IPC spans baseline
@@ -995,3 +1017,79 @@ SYMM CE prefix + SM current: gpu_p50=0.521ms cpu_p50=0.546ms
2. 当前 PyTorch symmetric-memory prototype 没有优于 CUDA IPC CE推测原因包括 symmetric memory backend/registration overhead、统一 window size 带来的空间浪费、以及仍使用 cudaMemcpyBatchAsync per-span submit。
3. 短期最有价值路径是继续用 IPC CE 做 prefix prefetch baseline同时保留 symmetric window helper 作为后续 NCCL CE/NVSHMEM transport 的地址合同实验入口。
4. 生产接入前必须继续做 span coalescing、跨 layer descriptor 复用,以及更大 bs/不同 cached/extend sweep不能只凭 symmetric window 概念直接替换现有路径。
---
## 10. FP8/RAGGED 与 index-skip prefetch 修正2026-06-12
### 10.1 远端日志现象
最新 prefill 日志中 `index_prefetch` fallback 被刷屏:
```text
[CP_SHARED_KV_FALLBACK][index_prefetch]
current_missing_prefetcher: 53136
not_paged_topk: 1296
```
排查结论:
1. `current_missing_prefetcher` 大多来自无 cache / tiny extend / prefetcher 未创建的正常同步路径不是运行时错误consume 侧不应每层每 rank warning。
2. `not_paged_topk` 是错误判定FP8 sparse prefill 走 `TopkTransformMethod.RAGGED` 是预期行为,但 index/MLA prefix materialize 仍是 page-slot 语义,不能因为 top-k transform 不是 PAGED 就关闭 prefetch。
### 10.2 当前修正
1. `CpSharedKVIndexPrefetcher.maybe_create()` 对 RAGGED top-k 不再 fallback而是继续创建 index prefetcher。
2. `CpSharedKVMlaPrefetcher.maybe_create()` 对 RAGGED top-k 也继续创建 MLA prefetcher。
3. `nsa_indexer._maybe_materialize_shared_index_buffer()` 不再对缺失 prefetcher 的正常同步路径打 warning仅在 prefetcher 已存在但 consume miss 时保留 fallback warning。
4. `nsa_backend.py` 的 FP8/RAGGED cache-hit compose 现在先尝试 `mla_prefetcher.consume_prefix_with_current()`,只有 prefetcher 存在但 miss 时才 warning 并回到同步 `materialize_prefix_and_reuse_current_kv_page_slots()`
5. attention-window 启动 index prefetch 时检查 `token_to_kv_pool.index_layer_to_slot`index-skip/shared 层没有 index-cache slot 时只跳过 index prefetch不影响 MLA prefetch。
### 10.3 验证
远端容器 `cjy-glm5-new` 已验证:
```text
python -m py_compile \
python/sglang/srt/layers/attention/nsa/cp_shared_kv_prefetch.py \
python/sglang/srt/layers/attention/nsa_backend.py
PYTHONPATH=python python -m pytest -q \
test/registered/unit/mem_cache/test_cp_shared_kv_runtime.py::TestCpSharedKVRuntimeHelpers::test_fp8_ragged_mla_uses_page_slot_current_compose_for_cache_hit \
test/registered/unit/mem_cache/test_cp_shared_kv_runtime.py::TestCpSharedKVRuntimeHelpers::test_mla_prefetch_create_allows_ragged_topk_transform \
test/registered/unit/mem_cache/test_cp_shared_kv_runtime.py::TestCpSharedKVRuntimeHelpers::test_index_prefetch_create_allows_ragged_topk_transform \
test/registered/unit/mem_cache/test_cp_shared_kv_runtime.py::TestCpSharedKVTaiMaterializeIntegration::test_attention_window_prefetch_skips_inactive_index_cache_layer
结果4 passed
```
### 10.4 剩余风险
1. 旧进程日志不会反映本次降噪和 RAGGED prefetch 修正;需要重启 prefill 后再观察 warning 频率。
2. RAGGED 当前层 materialize 的历史代码位置仍在 flashmla_sparse 分支内部;后续如果严格追求“当前层 materialize 完成后再启动下一层 prefetch”需要把 RAGGED current compose 前移到 attention-window hook 之前。
3. 本次只修正 create/consume 合同和单测;还需要 ETE 确认 FP8 cache-hit 下 index/MLA prefetch 命中率和精度不回退。
### 10.5 预期 skip 不再 warning2026-06-12
远端新进程仍出现大量 fallback warning 后,继续按日志聚合定位:
```text
[CP_SHARED_KV_FALLBACK][index_prefetch] reason=current_consume_miss 主要集中在 layer=0
[CP_SHARED_KV_FALLBACK][mla_prefetch] reason=ragged_current_consume_miss 主要集中在 layer=0
[CP_SHARED_KV_FALLBACK][index_prefetch|mla_prefetch] reason=prefix_not_page_aligned prefix_lens=[0]
```
结论:这些都不是异常 fallback
1. `layer=0` 没有上一层 attention-window 能提前发起本层 prefetchconsume miss 是预期同步路径。
2. `prefix_lens=[0]` 表示 request 本身没有命中 cacheprefix prefetch 不应创建。
3. 真实需要 warning 的场景应收窄为:非零 prefix 不 page-aligned、prefetcher 已创建且 `layer > start_layer` 仍 consume miss、metadata/slot 合同异常等。
修正合同:
1. MLA/index prefetch create 遇到全零 prefix 时只走 debug skip不再打 `[CP_SHARED_KV_FALLBACK]` warning。
2. index partial-current prefetcher 在 `layer == token_to_kv_pool.start_layer` miss 时不 warning。
3. FP8/RAGGED MLA prefetcher 在 `layer == start_layer` miss 时不 warning。
这类 skip 仍保留 debug 级别路径,方便需要时通过 debug 环境变量观察,但默认生产日志不应被无 cache/tiny/首层预期路径污染。
验证:远端 `cjy-glm5-new` 已通过 targeted regression3 passed以及完整 `test_cp_shared_kv_runtime.py`156 passed, 21 warnings, 2 subtests passed