Skip to content

Add KV-cache file prefetch plugin#1156

Open
leipanhz wants to merge 3 commits into
llm-d:mainfrom
leipanhz:feat/kv-cache-prefetch-plugin
Open

Add KV-cache file prefetch plugin#1156
leipanhz wants to merge 3 commits into
llm-d:mainfrom
leipanhz:feat/kv-cache-prefetch-plugin

Conversation

@leipanhz

Copy link
Copy Markdown
Contributor

Add KV-cache file prefetch plugin for inference requests (experimental feature)
Part 1: changes in KV-Cache
Part 2: changes in llm-d-router (current PR)

PR Description:
Introduces a new experimental feature that aims to proactively prefetch KV-cache blocks across different storage tiers before inference requests are processed by the GPU pod. The plugin extends the precise prefix cache scorer with engine key calculation to determine the storage location (file names) of KV-cache blocks that will be needed and arrange for them to be promoted to a closer storage tier to improve inference latency. The current implementation is intended for a shared file system that includes transparent access to a remote storage tier, such as IBM Storage Scale configured to off-load cold data to remote object storage. The prefetch plugin uses a concurrent worker thread pool architecture to efficiently prefetch multiple (configurable) files in parallel from remote storage to the shared file system. In a future version of the plugin this could be extended, for example, to prefetch KV-cache blocks from the file system to CPU memory on the worker node that the request is being routed to.

For this to work correctly, the plugin must be configured to use a hash algorithm for generating engine keys that matches the algorithm used by vLLM when offloading KV-cache blocks to storage. For this purpose, this work adds a configurable hashing algorithm SHA256-CBOR to the token processor as an alternative for vLLM compatibility. The SHA256-CBOR implementation supports extra keys (multimodal features) in block hash computation. In addition, this feature relies on logic derived from the llm-d-fs-connector to generate KV file names, so it currently only works with the llm-d-fs-connector.

Changes include:

New Prefetch Plugin (prefetch_prerequest_experimental.go):

Implements PreRequest interface for pre-inference file prefetching
Converts engine keys to filesystem paths using llm-d-fs-connector format
Manages worker thread pool for concurrent file prefetching (configurable workers)
Each worker reads configurable number of blocks (BlockSize x BlockCount bytes) from KV-cache files to trigger prefetch of the rest of the file from remote storage.
Supports configurable prefetch parameters (block size, concurrency, queue size)
Precise Prefix Cache Scorer Enhancement (precise_prefix_cache.go):

Add GetEngineKeysForRequest() method to extract engine keys from requests
Support multimodal features in engine key computation
Add SHA256-CBOR hashing algorithm for token processor with extra keys support

Add configuration to choose hashing function via the field name “hashAlgorithm”: FNV64a default, SHA256-CBOR for vLLM
Implement SHA256-CBOR hashing matching vLLM engine-key computation
Extend BlockExtraFeatures for multimodal content support

What type of PR is this?
/kind feature

What this PR does / why we need it:
See description in #866

Which issue(s) this PR fixes:
#866

Fixes #

Release note (write NONE if no user-facing change):

NONE

@github-actions

Copy link
Copy Markdown
Contributor

🚨 Unsigned commits detected! Please sign your commits.

For instructions on how to set up GPG/SSH signing and verify your commits, please see GitHub Documentation.

@github-actions github-actions Bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label May 15, 2026
@github-actions github-actions Bot requested review from elevran and vMaroon May 15, 2026 20:28
@leipanhz leipanhz force-pushed the feat/kv-cache-prefetch-plugin branch 5 times, most recently from 4633c91 to 9a7d956 Compare May 21, 2026 23:11
@elevran

elevran commented May 25, 2026

Copy link
Copy Markdown
Collaborator

@leipanhz can we please discuss the requirements/approach on #866 before continuing work on this PR?

@elevran elevran added the hold PRs that are blocked on design, other features, release cycle, etc. label Jun 4, 2026
Introduce a PreRequest plugin that proactively prefetches a request's
KV-cache files from a shared file system (e.g. IBM Storage Scale) before
the request reaches the GPU pod, promoting cold blocks to a closer tier
to reduce inference latency.

The plugin obtains the request's full-width block-hash digests from the
precise-prefix-cache-producer via GetEngineKeysAndDigestsForRequest and
maps each digest to its on-disk llm-d-fs-connector path, then reads the
head of each file on a worker pool to trigger the storage tier promotion.

File paths are constructed entirely from operator-supplied config rather
than filesystem discovery: rootDir, modelName, the fs-connector digest,
groupIdx, gpuBlocksPerFile, and the parallel sizes (tp/pp/pcp/dcp). The
rank space is iterated over [0, tp*pp*pcp*dcp); a file that is not yet
present is skipped without error.

Engine-key/digest computation lives on the producer: the shared
extra-features construction (multimodal hashes + cache salt) is factored
into buildBlockExtraFeatures so the engine-key and digest paths taint the
hash identically.

Signed-off-by: Lei Pan <leipan@ibm.com>
Co-authored-by: Yue Zhu <Yue.Zhu@ibm.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
@leipanhz leipanhz force-pushed the feat/kv-cache-prefetch-plugin branch from 9a7d956 to baf30cf Compare June 20, 2026 01:13
@leipanhz leipanhz requested review from a team and bongwoobak as code owners June 20, 2026 01:13
// across prompts and aligned index-for-index.
func (p *Producer) GetEngineKeysAndDigestsForRequest(_ context.Context,
request *scheduling.InferenceRequest) ([]uint64, [][]byte, error) {
engineKeys, digests, err := computeBlockKeysWithDigests(p.tokenProcessor, request, p.blockSizeTokens)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Enabling prefetch forces hashAlgorithm: sha256-cbor on the shared producer tokenProcessor, which also switches the requestKey/scoring path to sha256.

That collapses the requestKey<-->engineKey separation and puts sha256 on the scoring/event hot path even for non-prefetch traffic. Since the router only needs vLLM-matching hashes for prefetch filenames (the index already bridges engineKey-->requestKey for lookup),

could the engine-key/digest computation use a dedicated sha256-cbor hasher, leaving the scoring requestKey hasher independent (e.g. FNV)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @bongwoobak, you are correct that "the router only needs vLLM-matching hashes for prefetch filenames". The intention of using sha256-cbor as hashingAlgorithm is to offer user options to choose a different hashing algorithm other than the default FNV and reduce the complexity of using two hashing algorithms in the router. We had similar discussions with @vMaroon earlier, adding him here for additional comments or corrections if needed. Thank you

type KVFilePathBaseParams struct {
RootDir string `json:"rootDir"`
ModelName string `json:"modelName"`
Digest string `json:"digest"`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The digest is the fingerprint of the running vLLM's cache config, pinned statically in config here.

Isn't it effectively per-deployment it shifts whenever the backend's model/dtype/parallelism or vLLM version changes (e.g. a pod rolled out with a new config/version)? A stale value then silently no-ops with no error.

Could we read it dynamically when an endpoint is (re)discovered from a vLLM metric/label or similar?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The digest is the fingerprint of the running vLLM's cache config, pinned statically in config here.

Isn't it effectively per-deployment it shifts whenever the backend's model/dtype/parallelism or vLLM version changes (e.g. a pod rolled out with a new config/version)? A stale value then silently no-ops with no error.

Could we read it dynamically when an endpoint is (re)discovered from a vLLM metric/label or similar?

You suggestion is what we are looking for in the long run, having API support from vllm to provide config information. Currently there is no API (or metrics) available to retrieve the configuration content, a separately efffor is working to address this: vllm-project/vllm#38147. We are actively tracking this and
following up.

leipanhz added 2 commits July 7, 2026 15:54
Detect residency from st_blocks*512 vs the logical size: skip the
fault-in read when a file <= the buffer is fully resident, or when a
larger file already has more than a buffer's worth allocated. Tested
with IBM Storage Scale AFM, where reading the first buffer triggers a
full object fetch.

Signed-off-by: Lei Pan <leipan@ibm.com>
Extract submitForPrefetch and make submission unconditionally
non-blocking best-effort: drop the file immediately when the queue is
full. Remove the queueTimeout knob entirely -- there is no configuration
that puts waiting on the request path. A dropped prefetch is a benign
warm-up miss, so skipped is the queue-saturation signal.

Log the per-request digest-hashing duration at DEBUG. Refresh README
(non-blocking behavior, on-disk path layout, deployment config example
with volume mounts, on-path latency overhead) and add TestSubmitForPrefetch.

Signed-off-by: Lei Pan <leipan@ibm.com>
@leipanhz leipanhz force-pushed the feat/kv-cache-prefetch-plugin branch from 739c531 to 4f44b6f Compare July 11, 2026 01:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hold PRs that are blocked on design, other features, release cycle, etc. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

No open projects
Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants