The cache-hit accuracy drop is not isolated to one obvious kernel boundary, so the scheduler/radix/HiCache path needs request-correlated evidence. Add default-off, rate-limited CP shared-KV debug logs for prefix matching, valid-tail flooring, pending backup attach/commit, load-back planning, and unfinished-request cache insertion. The investigation notes capture current GSM8K pass1/pass2 evidence and rejected interpretations to avoid repeated log archaeology.
Constraint: Logs must be default-off and bounded because these paths are scheduler/control hot paths.
Rejected: Always-on INFO tracing | would distort the exact latency and cache-hit behavior under investigation.
Confidence: medium
Scope-risk: moderate
Directive: Remove or keep env-gated only after the GSM8K cache-hit root cause is closed; do not leave unbounded hot-path logs.
Tested: python -m py_compile on changed runtime files.
Not-tested: Local pytest blocked before collection by missing orjson dependency; fresh restarted GSM8K verification still pending.
(cherry picked from commit bfc6d1473dc2a5d72bc3a8d6fca1e2429537be0e)
4. first-run insert of `640 + short suffix` 到 second-run match `704/768` 的最小单测。
不要再重复优先排查 L2 load-back;本轮日志没有该路径参与证据。
## 2026-06-07 Z: root-cause direction refined to L1 persistent direct-write/read
Latest debug-enabled run (`20260607_112229`) shows:
- no `HiCache-load` / `load_back` / `CacheCtrl-load` events;
- no `FALLBACK` / `FAIL_FAST` / `RuntimeError`;
- second GSM8K run has far more device cache hits and short extends;
- second run consumes first-run suffix pages as L1 device prefix (`device_tokens=704/768/832`, `host_hit_length=0`).
This makes L2 load-back unlikely for the accuracy drop. A stronger hypothesis is:
1. first run computes prompt/current suffix correctly because attention/index paths use freshly produced current KV/index rows in partial-current compose;
2. first run also writes those rows into the persistent L1 CP shared-KV pool through direct-write/fused-store;
3. second run hits those persisted device pages and reads them as prefix;
4. if direct-write layout/offset/FP8 packing is wrong, first run can remain accurate while second run drops.
Next inspection target: direct-write store/read roundtrip for `page_first_direct`, `fp8_e4m3`, batch/compute-padding, especially MLA KV fused store and NSA index K/scale store. Do not spend more cycles on L2 load-back for this run unless new logs show `HiCache-load`.
## 2026-06-07 AA: cp_floor_exact false is internal refresh; transfer confirms second run sends fully cached prompts
Latest log recheck for `20260607_112229`:
- `cp_floor_exact=False` `match_prefix_result` events all have `rid=None`:
- Scheduler-visible request matches (`rid != None`) are page-aligned. The non-page results are from the post-insert/cache refresh path and are not direct scheduler planning inputs.
- First GSM8K run CP0 send shape:
- 1318/1319 requests transfer with `fill_len - extend_input_len = 640`.
- Average extend sent/computed ≈ 92 tokens.
- Second GSM8K run CP0 send shape:
- prefix distribution from `fill_len - extend_input_len`: `704:1154`, `768:82`, `640:81`, `832:2`.
- Average extend ≈ 27 tokens.
- `send_kv_chunk` runs after cache insertion refresh; its logged `prefix_len=len(req.prefix_indices)` often equals full `fill_len` in the second run, so decode is receiving page tables that can be entirely first-run cached prompt pages, not just freshly computed tail rows.
Implication: the current strongest path is not L2 load-back and not scheduler non-page prefix planning. The second run transfers and consumes first-run persisted L1 prompt pages (question suffix pages) far more aggressively. Root-cause search should focus on persistent L1 writes/readback/transfer for bs>1 compute-padding FP8 CP shared KV:
2. NSA index fused persistent store -> cached prefix materialize / top-k.
3. Radix post-insert refresh replacing freshly computed tail with full cached prompt before transfer.
Need a focused test that writes bs=5 compute-padded valid rows into persistent L1, then reads/materializes the same pages as a later cache-hit prefix. Existing tests cover index store+materialize and MLA store-vs-fallback, but do not yet prove FP8 MLA persistent pages survive the second-run cache-hit materialize/transfer shape.
This weakens the hypothesis that the ordinary fused MLA store + local materialize + all-reduce path corrupts bs=5 FP8 persistent L1 pages. Remaining likely areas:
1. runtime path difference not covered by the unit, especially TAI IPC materialize when a single prefix span is used;
2. decode/disaggregation transfer consuming cached prompt pages after `cache_finished_req` refreshes `req_to_token` to full cached prompt;
3. draft/target transfer parity for cached-prefix pages;
4. index runtime path still has a CUDA persistent-page test, but it disables IPC and may not cover the exact online materialized shared-buffer path with all environment flags.
## 2026-06-07 AC: new process ready; raw-output based failure classification required
New remote prefill log is `/mnt/beegfs/cjy/log/sglang_cp_hicache_20260607_131732.log`.
Startup evidence:
- server reached `The server is fired up and ready to roll!` at log line 4334;
- no startup `Traceback` / `RuntimeError` / `FAIL_FAST` / `FALLBACK` marker before readiness;
- fp8 KV cache still warns that no scaling factors were provided and scaling defaults to 1.0 on all ranks. This warning alone cannot explain the second-run-only accuracy drop because first-run fp8 accuracy was normal, but it remains part of the runtime context.
Existing raw result file found:
- `/mnt/beegfs/cjy/gsm8k_bench_sglang_full_raw_20260606_132651.jsonl`: 1319 rows, accuracy 0.955; its failures are ordinary wrong GSM8K reasoning/answers, not obvious repetitive gibberish. No matching second-run raw file was found yet, so cache-hit drop must be probed with new raw-result files.
Next diagnostic: run two small GSM8K passes (50-200 questions) with `--raw-result-file`, then compare first vs second failures and prefill `match_prefix_result` / `prefill_result_handoff` / `send_kv_chunk` around that window.
## 2026-06-07 AD: 200-question raw probe reproduces second-run drop and shows prompt/request contamination pattern
Probe command shape: two sequential GSM8K passes with `--num-questions 200 --parallel 64 --raw-result-file`, on the new process/log `/mnt/beegfs/cjy/log/sglang_cp_hicache_20260607_131732.log`.
Important qualitative signal: second-run failures are usually not random gibberish. Some outputs answer a different problem or echo a few-shot/example prompt fragment:
- id=33 prompt asks Gretchen coins, output starts with the standard Janet-ducks GSM8K example and answers `18`;
- id=45 prompt asks Meredith blogging hours, output starts with `20\nKylie makes and sells scarves...` and answers `180`;
- id=62 prompt asks Marcy 30-year pension, output switches to a 25-year pension variant and answers `31250`;
- id=75 prompt asks average square footage, output answers a similar total-square-footage problem.
Prompt-echo marker count:
```text
pass1 total_echo=1, fail_echo=0
pass2 total_echo=18, fail_echo=15
```
This points away from pure fp8 numerical drift and toward request/prompt metadata contamination or offset/order mismatch under bs>1 cache-hit. The model is often coherent but conditioned on the wrong cached prompt slice / wrong hidden/topk / wrong transferred page table.
Conclusion update: root cause is likely in the bs>1 cache-hit online path where per-request cached prefix pages, output hidden/topk, and decode transfer metadata must stay aligned. Continue checking:
1. `process_batch_result_disagg_prefill`: `batch.reqs`, `result.next_token_ids`, and `batch.spec_info.{hidden_states,topk}` order agreement;
2. `cache_unfinished_req` side effects before `send_kv_chunk` (it can refresh `req_to_token`/`prefix_indices` to a fuller cached prompt);
3. `send_kv_chunk` target/draft `page_indices/state_indices` consistency with the hidden/topk assigned to that same req;
4. any batch-level plan that reorders/splits valid rows without carrying request ids through to logits/sample/spec tensors.
## 2026-06-07 AE: failing unit test isolates compute-padding all-gather rerange source-offset bug
Added a targeted CPU unit test for this exact dataflow:
- request 0 is a tiny extend that enables compute padding;
- request 1 follows it in the same bs>1 rank-major all-gather buffer;
- valid output must drop request-0 synthetic rows, but source offsets must still skip those synthetic rows.
Root-cause candidate is now concrete: `_torch_batch_in_seq_all_gather_rerange()` used `request_split_lists` (valid rows) to compute source offsets inside the rank-major all-gather payload. Under compute padding, the source payload is laid out by `request_compute_split_lists`, while output must remain `request_split_lists`. This can make the second request read the first request's padded mirror rows or another request's rows after cache-hit/tiny-extend batching, matching the observed GSM8K prompt-fragment contamination.
Required contract:
- source rank offsets: compute split lists when `compute_padding_enabled`;
- output length/order: valid split lists;
- copy length per segment: valid segment length;
- no synthetic compute-padding row may appear in restored hidden/index/MLA rows.
Fix applied:
- `_torch_batch_in_seq_all_gather_rerange()` now separates `source_split_lists` from `output_split_lists`;
- source rank offsets and mirror offsets use `request_compute_split_lists` when compute padding is enabled;
- output allocation, output prefixes, and copy lengths still use valid `request_split_lists`;
- fail-fast validates that every compute segment covers the corresponding valid segment.
ETE implication to verify after restart: if this was the second-run cache-hit accuracy root cause, a 200-question two-pass GSM8K probe should no longer show pass2 dropping to ~0.70 nor show prompt-fragment echo spikes.
which selects the first `extend_input_len` prompt locations, i.e. prefix page slots, not the suffix locations at `pre_len : pre_len + extend_input_len`.
- In pass1, many requests have low cache hit / larger prefill suffix, so the wrong prebuilt suffix mapping is less concentrated on short cache-hit tails.
- In pass2, `pre_len` is much larger and `extend_input_len` is often tiny; using `[:extend_input_len]` makes decode update/read the beginning of the prompt instead of the actual tail.
- Raw outputs looked like the model saw the first part of the question but missed final constraints (`per week`, `$3 fee + tip`, `4 weeks`, etc.), which is consistent with the tail KV being mapped to the wrong cache locs.
Planned fix: slice decode prebuilt locs with `pre_len : pre_len + req.extend_input_len`, matching the existing input-id and assertion contract. This is not CP-specific; it is a general cache-hit prebuilt correctness bug that CP shared-KV bs>1 exposed by making second-pass cache hits much deeper/shorter.
# The cache_protected_len is not always equal to len(req.prefix_indices)
# since for page_size > 1, the partial part is added to req.prefix_indices, but that part of kv indices is not added to the tree.
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.