fix(qwen3): reclaim completed KV blocks when prefix cache is off (#681)#735
fix(qwen3): reclaim completed KV blocks when prefix cache is off (#681)#735Polaris3003 wants to merge 1 commit into
Conversation
xiaguan
left a comment
There was a problem hiding this comment.
Thanks for the careful lifecycle work here. The direction makes sense: when prefix matching is disabled, completed-request blocks should not remain in the inactive cache, and following a duplicate's keepalive to mark the canonical primary addresses the hidden-capacity case from #681.
The low-level coverage is useful. I ran cargo test -p kvbm-logical -p openinfer-kv-cache --lib locally: all 495 kvbm-logical tests and all 5 openinfer-kv-cache tests passed, including the new duplicate-primary and issue-shaped capacity cases.
Before approval, could you please add contributor-run E2E evidence for the affected Qwen3 path:
- Reproduce #681 on the baseline and show this branch completing the same sequential 161-block /
--gpu-memory-utilization 0.85workload. Please cover both sampled and greedy DFlash, since the issue reproduces on both paths, and include enough consecutive requests to show capacity remains reusable. - Run the affected Qwen3
prefix_cacheintegration test and HF golden gate, with the exact commands and pass summaries. - Let the repository CI complete successfully; the CI run is currently
action_required, so only the attribution check has executed.
These results will connect the sound CPU lifecycle model to the actual GPU executor path and verify that disabling retention fixes the reported failure without regressing prefix-cache-enabled serving.
Signed-off-by: Polaris <2608726255@qq.com>
ef18afc to
35d31d9
Compare
|
Thanks — here is the contributor-run E2E and integration-test evidence for #681. Environment
Because this GPU has 24 GiB rather than 16 GiB, literal This is therefore a capacity-equivalent 161-profiled-block reproduction, rather than a hardware-identical DFlash E2E commandThe server was restarted between every case: RUST_LOG=info <baseline-or-PR-binary> \
--model-path /root/openinfer/models/Qwen3-4B \
--served-model-name qwen3-4b \
--port 18071 \
--gpu-memory-utilization 0.502 \
--no-prefix-cache \
--dflash-draft-model-path /root/openinfer/models/Qwen3-4B-DFlash-b16Each mode ran 20 sequential requests: uv run --no-project \
--with datasets \
--with requests \
--with tqdm \
--with transformers \
python scripts/eval_gsm8k_thinking.py \
--base-url http://127.0.0.1:18071/v1/completions \
--model qwen3-4b \
--tokenizer /root/openinfer/models/Qwen3-4B \
--num-fewshot 8 \
--limit 20 \
--max-tokens 512 \
--seed -1 \
--temperature <0.8-for-sampled-or-0-for-greedy> \
--output <case-result.json>Results:
This reproduces #681 on both baseline paths and shows the PR reusing the constrained KV capacity across consecutive requests. One evaluator detail: the greedy invocation completed all 20 requests and wrote all 20 results, but Qwen3 prefix-cache integration testExact command: OPENINFER_TEST_MODEL_PATH=/root/openinfer/models/Qwen3-4B \
cargo test --release \
-p openinfer-qwen3 \
--test prefix_cache \
-- --nocaptureResult: Qwen3 HF golden gateExact command: OPENINFER_TEST_MODEL_PATH=/root/openinfer/models/Qwen3-4B \
cargo test --release \
-p openinfer-qwen3 \
--test hf_golden_gate \
-- --nocaptureResult: Repository CI remains a separate gate; it still needs maintainer approval and successful completion. |
Qwen3 DFlash disables prefix matching so the target prefill produces fresh hidden states for the draft model, but disabling matching did not disable retention of completed request blocks. Registered blocks entered kvbm's inactive cache even though no later DFlash request can match them; when a later request recomputed the same hash, BlockDuplicationPolicy::Allow registered a duplicate that pinned the old primary via _primary_keepalive, leaving it physically resident but invisible to scheduler accounting. Near capacity, speculative verification then failed to allocate its next page.
Add ImmutableBlock::set_primary_evict_on_reset to mark the canonical primary (followed through a duplicate's keepalive) for reset-on-release, and RequestKv::mark_blocks_reset_on_release to apply it across a request's registered assignments. Qwen3 centralizes the retention decision in retains_completed_kv_blocks / finish_retention_transition: enabling->disabling drains the inactive pool once, and drop_request marks a completed request's blocks for reset when retention is off. Prefix-cache-enabled serving keeps its existing inactive-cache reuse behaviour.
Description
Fixes #681 (issue number)
Type of Change
Checklist
docs/conventions/coding-style.md).CLAUDE.md).