Skip to content

[KVOffload] Document CPU eviction behavior as reference (control branch) - #47234

Open
Alex-ai-future wants to merge 4 commits into
vllm-project:mainfrom
Alex-ai-future:fix/cpu-eviction-tracking2
Open

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

Conversation

@Alex-ai-future

Copy link
Copy Markdown
Contributor

Purpose

Document and test the current SimpleCPUOffloadScheduler eviction behavior
as a reference for the FIXME: num_stored_blocks can be stale and omit evicted blocks in the middle of the request.

This branch is a control/reference. It does not change the eviction
algorithm. Instead it explains why the current behavior is safe and provides
tests that prove the documented properties.

Problem

After _process_store_completion() frees CPU blocks (ref_cnt → 0), they
join the free queue tail (MRU). A subsequent get_new_blocks() call can
re-allocate these blocks, removing their cache entries via
_maybe_evict_cached_block(). This can happen while the original request is
still active — its num_stored_blocks cursor has advanced past the evicted
blocks and they will never be re-stored.

Why this is safe

The trade-off is intentional: re-scanning from block 0 every step would turn
this loop from O(new blocks) into O(total blocks), wasting bandwidth on
redundant store attempts.

Safety mechanism What it does
Load path grace find_longest_cache_hit() returns a shorter match; missing tokens are recomputed from GPU — no data loss
LRU ordering Freed blocks go to the MRU tail; active request blocks are the LAST to be evicted
In-flight protection Blocks with ref_cnt=1 during async DMA leave the free queue entirely
Preemption reset num_stored_blocks resets to 0 on preemption, causing evicted blocks to be re-scanned

Changes

File Changes
vllm/v1/simple_kv_offload/manager.py Rewrite NOTE to explain the trade-off and all safety mechanisms
tests/v1/simple_kv_offload/test_scheduler.py Add 2 tests (see below)

Test Plan

.venv/bin/python -m pytest 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_in_flight_store_protected In-flight blocks (ref_cnt=1) leave the free queue; when num_free=0 the store loop defers (out_of_space) rather than evicting
test_active_request_blocks_can_be_evicted Active (not finished) request blocks CAN be evicted after store completion — proves the FIXME's scenario is real

Test Result

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

2 passed, 16 warnings in 4.89s

ruff: All checks passed.

Limitations

  • No code fix: This branch intentionally does not fix the FIXME. It
    documents the current behavior as a reference point for evaluating the
    fix-cpu-eviction-tracking branch which adds re-store logic.
  • Active request blocks can be evicted: This is by design, not a bug. The
    NOTE explains why the trade-off (O(1) cursor vs. re-scan cost) is acceptable.

The FIXME claimed num_stored_blocks cursor could become stale when CPU
LRU evicts blocks in the middle of a request. Analysis shows this
scenario cannot occur:

1. Active request blocks (ref_cnt > 0) are not in the free queue,
   so LRU cannot evict them.
2. Finished request blocks (ref_cnt == 0) can be evicted, but their
   offload state is already cleaned up, so the scan loop skips them.

Replace the FIXME with a NOTE explaining the safety argument, and add
test_active_request_blocks_not_evicted which proves that blocks with
ref_cnt > 0 survive LRU eviction while ref_cnt == 0 blocks do not.

Signed-off-by: jihuihuang
Signed-off-by: Alex <alex.tech.lab@outlook.com>
The original NOTE claimed active request blocks (ref_cnt > 0) cannot be
evicted, but conflated 'active request' with 'ref_cnt > 0'. After
_process_store_completion() frees blocks, ref_cnt drops to 0 and blocks
become LRU eviction candidates even for active requests.

Rewrite the NOTE with accurate reasoning:
1. In-flight blocks (ref_cnt=1 during async DMA) leave free queue; when
   num_free=0 the store loop defers (out_of_space) rather than evicting.
2. Post-completion blocks (ref_cnt=0) can be evicted; the load path
   handles this gracefully via shorter cache hits.
3. Preemption resets the cursor, causing re-scan of evicted blocks.
4. Finished requests are skipped (state is None or finished).

Replace the broken test (touch() on req_b was a no-op because req_a
was already at the LRU head) with a real in-flight store test that
proves the out_of_space deferral mechanism.

Signed-off-by: Alex <alex.tech.lab@outlook.com>
The FIXME worried that evicted blocks below the cursor would be silently
lost. Rewrite the NOTE to explain why this is an intentional O(1) vs
O(N) trade-off: re-scanning from block 0 every step would waste
bandwidth on redundant store attempts.

Key clarification: active request blocks CAN be evicted after store
completion (ref_cnt drops to 0). They are protected by LRU ordering
(MRU tail, evicted last) but not by ref_cnt. The load path handles
eviction gracefully via shorter cache hits and GPU recomputation.

Add test_active_request_blocks_can_be_evicted to prove the FIXME's
scenario: an active (not finished) request's stored blocks are evicted
by a subsequent store. This validates NOTE claim vllm-project#2.

Signed-off-by: Alex <alex.tech.lab@outlook.com>
@Alex-ai-future
Alex-ai-future requested a review from ivanium as a code owner July 1, 2026 02:15

@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.

Signed-off-by: Alex <alex.tech.lab@outlook.com>
@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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant