Skip to content

[Bugfix][KVOffload] Restore evicted CPU blocks and fix cursor advancement - #47235

Draft
Alex-ai-future wants to merge 2 commits into
vllm-project:mainfrom
Alex-ai-future:fix/cpu-eviction-tracking
Draft

Alex-ai-future wants to merge 2 commits into
vllm-project:mainfrom
Alex-ai-future:fix/cpu-eviction-tracking

Conversation

@Alex-ai-future

Copy link
Copy Markdown
Contributor

Purpose

Add CPU eviction tracking to SimpleCPUOffloadScheduler to re-store blocks whose
cache entries were lost when CPU blocks are re-allocated, avoiding unnecessary
re-prefill for requests with matching prefixes.

Resolves the FIXME: num_stored_blocks can be stale and omit evicted blocks in the middle of the request

Problem

When CPU blocks are freed after store completion (ref_cnt → 0) and later
re-allocated by get_new_blocks(), their cache entries are removed by
_maybe_evict_cached_block(). This can happen while the original request is
still active — CPU and GPU ref_cnts are independent. Without re-store,
subsequent requests with matching prefixes miss the CPU cache and need to
re-prefill.

Solution

  • Eviction signal: Add consume_eviction_signal() to BlockPool — atomically
    reads and resets an eviction flag set by _maybe_evict_cached_block(). The
    manager consumes this signal once per step before the request loop.

  • Phase 1a re-scan: When eviction is detected, re-scan previously stored
    blocks (0..already_stored_g) and re-store any that lost their cache entries.
    Phase 1b continues to scan newly ready blocks (already_stored_g..ready_blocks_g).

  • Cursor advancement: Use absolute position semantics — initialize
    advanced_per_group[g] to already_stored_g, Phase 1a does not modify it,
    Phase 1b adds delta. Final assignment: state.num_stored_blocks[g] = advanced_per_group[g].

  • Helper extraction: Extract _should_skip_block() for reuse between phases.

Changes

File Changes
vllm/v1/core/block_pool.py Add _had_recent_eviction flag and consume_eviction_signal() method
vllm/v1/simple_kv_offload/manager.py Consume signal per step; Phase 1a (re-scan evicted) + Phase 1b (scan new); absolute position cursor; extract _should_skip_block(); update NOTE
tests/v1/simple_kv_offload/test_scheduler.py Add 4 tests (see below)

Impact

Aspect Before After
Evicted block re-store Blocks never re-stored (FIXME) Re-stored on next step
Cursor complexity O(n) per step O(n) per step
Phase 1a overhead N/A Zero when no eviction

Test Plan

.venv/bin/python -m pytest tests/v1/simple_kv_offload/test_scheduler.py::test_eviction_signal_set_and_consumed tests/v1/simple_kv_offload/test_scheduler.py::test_phase1a_restore_enables_cache_hit tests/v1/simple_kv_offload/test_scheduler.py::test_in_flight_store_protected tests/v1/simple_kv_offload/test_scheduler.py::test_active_request_blocks_can_be_evicted -v
Test What it verifies
test_eviction_signal_set_and_consumed Flag is set on eviction and reset on consume
test_phase1a_restore_enables_cache_hit End-to-end: after eviction + re-store, new request with same prefix gets full CPU cache hit (no re-prefill)
test_in_flight_store_protected In-flight blocks are not evicted by concurrent stores
test_active_request_blocks_can_be_evicted Active request blocks CAN be evicted after store completion (CPU ref_cnt is independent of GPU ref_cnt)

Test Result

tests/v1/simple_kv_offload/test_scheduler.py::test_eviction_signal_set_and_consumed PASSED
tests/v1/simple_kv_offload/test_scheduler.py::test_phase1a_restore_enables_cache_hit PASSED
tests/v1/simple_kv_offload/test_scheduler.py::test_in_flight_store_protected PASSED
tests/v1/simple_kv_offload/test_scheduler.py::test_active_request_blocks_can_be_evicted PASSED

4 passed, 16 warnings in 7.27s

ruff: All checks passed.

Limitations

  • Trigger scenarios are relatively rare: eviction only occurs when CPU blocks
    from a previous store are re-allocated by get_new_blocks(), which requires
    high CPU memory pressure or many concurrent stores. When it does occur, Phase 1a
    ensures re-store happens.
  • Cross-file changes: touches both block_pool.py (signal API) and
    manager.py (Phase 1a/1b logic) — the split is intentional as a producer-consumer
    contract between the two modules.

Alternative approach

If reviewers consider the above limitations too significant for the rare trigger
scenario, an alternative is to document the current eviction behavior as a known
property rather than adding re-store logic:
#47234 — documents CPU
eviction behavior as reference (control branch).

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@mergify mergify Bot added the v1 label Jul 1, 2026
@Alex-ai-future

Copy link
Copy Markdown
Contributor Author

I think this pr is ready for review, hope to get your suggestions~
CC @ivanium @orozery

…ancement

Add CPU eviction tracking to SimpleCPUOffloadScheduler to re-store blocks
whose cache entries were lost when CPU blocks are re-allocated, avoiding
unnecessary re-prefill for requests with matching prefixes.

Resolves the FIXME: num_stored_blocks can be stale and omit evicted blocks
in the middle of the request

- Add consume_eviction_signal() to BlockPool — atomically reads and resets
  an eviction flag set by _maybe_evict_cached_block()
- Split scanning into Phase 1a (re-scan evicted) + Phase 1b (scan new)
- Fix cursor advancement with absolute position semantics
- Extract _should_skip_block() helper for reuse between phases

Signed-off-by: Alex <alex.tech.lab@outlook.com>
- test_eviction_signal_set_and_consumed: flag lifecycle
- test_phase1a_restore_enables_cache_hit: end-to-end cache hit after re-store
- test_in_flight_store_protected: in-flight blocks not evicted
- test_active_request_blocks_can_be_evicted: active request blocks CAN be evicted

Signed-off-by: Alex <alex.tech.lab@outlook.com>
@Alex-ai-future
Alex-ai-future force-pushed the fix/cpu-eviction-tracking branch from fbb20b3 to dcdb332 Compare July 1, 2026 03:08
@Alex-ai-future Alex-ai-future changed the title fix(simple_kv_offload): restore evicted CPU blocks and fix cursor advancement [Bugfix][KVOffload] Restore evicted CPU blocks and fix cursor advancement Jul 3, 2026
@mergify mergify Bot added the bug Something isn't working label Jul 3, 2026
@Alex-ai-future
Alex-ai-future marked this pull request as draft August 24, 2026 07:11
@mergify

mergify Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @Alex-ai-future.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant