Skip to content

Optimize LRU recency ordering to preserve contiguous KV prefixes #396

Description

@GentleCold

Problem

The resident KV cache currently updates LRU recency in request block order.
For a request with blocks a, b, c:

  • the save path inserts a -> b -> c;
  • the prefix lookup path accesses a -> b -> c.

This leaves the prefix block a older than the suffix block c. Under memory pressure, a can be evicted first, leaving b, c resident but unusable for prefix lookup because lookup stops at the first missing block.

Relevant paths:

  • pegaflow-core/src/offload.rs: builds insert entries in block order;
  • pegaflow-core/src/storage/read_cache.rs: inserts batches and scans prefix hits in block order;
  • pegaflow-core/src/cache.rs: removes the least-recently-used block.

Desired behavior

Within the blocks of one request prefix, suffix blocks should become eviction candidates before earlier prefix blocks. For a, b, c, memory pressure should prefer evicting c, then b, then a, preserving the longest contiguous prefix possible.

The external query/result order must remain a, b, c; only cache recency updates should change.

Chunked prefill caveat

Simply reversing each save batch is insufficient because chunked prefill saves only newly completed blocks.

For example:

  1. chunk 1 saves a, b;
  2. chunk 2 saves c, d.

Reversing each batch independently does not refresh a, b when c, d arrive, so an older chunk can still be evicted before the latest suffix. Chunked prefill should have eviction behavior equivalent to a one-shot save of a, b, c, d, without repeating GPU-to-host copies for blocks already resident.

Possible direction

  • Determine the contiguous prefix in request order without changing its semantics.
  • Touch the matched resident blocks in reverse order (suffix -> prefix) so earlier prefix blocks are most recent.
  • Insert blocks from a complete save batch in reverse order.
  • Add a lightweight way for chunked prefill to refresh the complete resident prefix when a new chunk is appended, rather than re-saving existing KV data.

This is a direction, not a required API design; a request-aware eviction policy that provides the same behavior would also work.

Acceptance criteria

  • Prefix lookup still returns blocks in request order and stops at the first miss.
  • For an otherwise equally accessed a, b, c prefix, eviction prefers c, then b, then a.
  • Saving a, b followed by c, d through chunked prefill has the same within-request eviction priority as saving a, b, c, d in one operation.
  • Existing blocks are not copied from GPU to host again solely to update recency.
  • TinyLFU admission and cross-request LRU behavior remain valid.
  • A focused test covers both one-shot and chunked-prefill ordering.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions