88// (no CUDA, no model): the physical KV memory itself is not touched here, only
99// the block bookkeeping.
1010//
11- // VERSION NOTE (which upstream is mirrored): the landed kv_cache_utils.{h,cpp}
12- // mirror the CLASSIC vLLM V1 block-hashing API — free functions
13- // hash_block_tokens / hash_request_tokens that take a pluggable hash_function,
14- // BlockHash as raw hash bytes, and make_block_hash_with_group_id packing a
15- // 4-byte group id. BlockPool is ported to match THOSE utils, i.e. the classic
16- // BlockPool shape:
17- // - cached_block_hash_to_block is a plain
18- // {BlockHashWithGroupId -> {block_id -> KVCacheBlock*}} nesting (the classic
19- // defaultdict(dict)); the e24d1b24 BlockHashToBlockMap single-vs-dict union
20- // optimization is intentionally NOT ported — it is a GC micro-optimization
21- // with identical observable semantics.
22- // - cache_full_blocks carries the classic (block_hashes, hash_fn) parameters:
23- // the caller passes the precomputed per-block hashes (from
24- // hash_request_tokens) and a hash function used to fill in any trailing
25- // blocks whose hashes were not precomputed. The e24d1b24 signature instead
26- // reads request.block_hashes (a Request field not present in the T0 Request
27- // port) and takes hash_block_size on the pool; that path is not portable
28- // against the landed utils, so the classic signature is used.
29- // The @ e24d1b24 pin marks the upstream tree tracked for the surrounding V1
30- // core; the BlockPool logic mirrored is upstream's classic pre-BlockHashToBlockMap
31- // form, consistent with the already-landed classic-hashing utils.
11+ // API FIDELITY: this mirrors the e24d1b24 BlockPool API 1:1 for the COMMON
12+ // prefix-caching path. cache_full_blocks takes the pinned signature
13+ // (request, blocks, num_cached_blocks, num_full_blocks, block_size,
14+ // kv_cache_group_id, block_mask) and reads request.block_hashes — the per-block
15+ // hashes the Request computes incrementally via its _block_hasher
16+ // (get_request_block_hasher). The pool carries hash_block_size (per the pin).
3217//
33- // DEFERRED, recorded (kept as stubs / no-ops so signatures stay 1:1 and the
34- // later units fill them in without a call-site change):
18+ // DEFERRED behind 1:1 stubs (documented; the gate models — text-only GDN/MoE —
19+ // never exercise these, and later units fill them in without a call-site change):
20+ // - The align / multiple-block-size path in cache_full_blocks
21+ // (block_size != hash_block_size, upstream's BlockHashListWithBlockSize):
22+ // guarded with a throw. Only the block_size == hash_block_size common case
23+ // is ported.
24+ // - The partial->full promotion branch inside cache_full_blocks (a "new full
25+ // block" that already carries a hash): guarded with a throw. Requires the
26+ // deferred partial primitives.
27+ // - cache_partial_block / _get_partial_block_hash /
28+ // _get_partial_block_parent_hash_and_start: throw-if-called stubs (partial
29+ // prefix entries, not needed by the gate models).
30+ // - evict_blocks (KV-connector-driven cache eviction): throw-if-called stub.
3531// - KV-cache events: enable_kv_cache_events, kv_event_queue, take_events(),
36- // and the BlockStored / BlockRemoved emission inside cache_full_blocks /
37- // _maybe_evict_cached_block. KVCacheEvent is a placeholder struct; the
38- // emission branches are omitted (marked in the .cpp). This mirrors upstream
39- // vllm/distributed/kv_events.py, deferred with the rest of the V1 event bus .
32+ // and the BlockStored / BlockRemoved / AllBlocksCleared emission inside
33+ // cache_full_blocks / _remove_cached_block_hashes / reset_prefix_cache.
34+ // KVCacheEvent is a placeholder struct; the emission branches are omitted
35+ // (marked in the .cpp). Mirrors upstream vllm/distributed/kv_events.py .
4036// - metrics_collector (KVCacheMetricsCollector): the on_block_allocated /
41- // _accessed / _evicted hooks are omitted. Not part of the correctness core.
42- // - cache_partial_block / evict_blocks: not in the classic shape and not
43- // needed by the gate models; omitted.
37+ // _accessed / _evicted / reset hooks are omitted. Not part of the
38+ // correctness core.
39+ // - block_mask: the parameter is present and honored (masked blocks skipped),
40+ // but only ever passed None by the ported (non-SWA/non-mamba) call sites.
41+ //
42+ // DEVIATION, recorded: the BlockHashToBlockMap single-block-vs-dict union
43+ // (upstream's GC micro-optimization) is NOT ported. cached_block_hash_to_block
44+ // is the plain {BlockHashWithGroupId -> {block_id -> KVCacheBlock*}} nesting.
45+ // Observable semantics are identical (get_one_block / contain / insert / pop
46+ // map onto the nested-map operations); only the inner GC cost differs.
4447//
4548// DEVIATIONS, recorded:
4649// - The null block (block_id 0) is made un-allocatable exactly as upstream:
6467#include < cstdint>
6568#include < map>
6669#include < optional>
70+ #include < set>
6771#include < unordered_map>
6872#include < vector>
6973
@@ -88,8 +92,11 @@ class BlockPool {
8892 // Args:
8993 // num_gpu_blocks: The number of blocks in the pool (must be > 0).
9094 // enable_caching: Whether to enable prefix caching.
95+ // hash_block_size: The block size at which block hashes are computed. The
96+ // actual block size usually equals it; with differently-sized KV cache
97+ // groups it can be a multiple (the align path, DEFERRED).
9198 // enable_kv_cache_events: Whether to enable kv cache events (DEFERRED).
92- BlockPool (int64_t num_gpu_blocks, bool enable_caching,
99+ BlockPool (int64_t num_gpu_blocks, bool enable_caching, int hash_block_size,
93100 bool enable_kv_cache_events = false );
94101
95102 // Non-copyable / non-movable (see DEVIATIONS in the file header).
@@ -104,25 +111,39 @@ class BlockPool {
104111 std::optional<std::vector<KVCacheBlock*>> get_cached_block (
105112 const BlockHash& block_hash, const std::vector<int >& kv_cache_group_ids);
106113
107- // Cache a list of full blocks for prefix caching. Updates each newly-full
108- // block's hash metadata and inserts it into cached_block_hash_to_block.
114+ // Cache a list of full blocks for prefix caching. Reads request.block_hashes
115+ // (computed incrementally by the Request's _block_hasher), updates each
116+ // newly-full block's hash metadata and inserts it into
117+ // cached_block_hash_to_block.
109118 //
110- // block_hashes: the request's per-block hashes; block_hashes[k] is the hash
111- // of the k-th full block. May be extended in place for trailing blocks
112- // whose hash was not precomputed (the else branch computes it via hash_fn
113- // and appends it — matching upstream, which caches it on the request for a
114- // possible future preemption).
119+ // request: the request whose block_hashes drive caching.
120+ // blocks: all blocks in the request (the range
121+ // [num_cached_blocks, num_full_blocks) is cached).
115122 // num_cached_blocks: number of blocks already cached.
116123 // num_full_blocks: number of blocks that are full and should be cached now.
117124 // block_size: number of tokens per block.
118125 // kv_cache_group_id: id of the KV cache group.
119- // hash_fn: the pluggable block-hash function (see kv_cache_utils.h).
120- void cache_full_blocks (const Request& request,
121- const std::vector<KVCacheBlock*>& blocks,
122- std::vector<BlockHash>& block_hashes,
123- int num_cached_blocks, int num_full_blocks,
124- int block_size, int kv_cache_group_id,
125- const HashFn& hash_fn);
126+ // block_mask: optional mask aligned with blocks[num_cached_blocks:
127+ // num_full_blocks]; masked-off (false) blocks are skipped like null blocks.
128+ // Only ever None from the ported call sites (see header DEFERRED note).
129+ //
130+ // Common path only (block_size == hash_block_size); the align path and the
131+ // partial->full promotion branch are DEFERRED (throw). See the header.
132+ void cache_full_blocks (
133+ const Request& request, const std::vector<KVCacheBlock*>& blocks,
134+ int num_cached_blocks, int num_full_blocks, int block_size,
135+ int kv_cache_group_id,
136+ const std::optional<std::vector<bool >>& block_mask = std::nullopt );
137+
138+ // Register a partial prefix-cache entry for an existing block. DEFERRED 1:1
139+ // stub (partial primitives, not needed by the gate models): throws if called.
140+ std::optional<BlockHashWithGroupId> cache_partial_block (
141+ const Request& request, KVCacheBlock* block, int num_tokens,
142+ int kv_cache_group_id, int block_size);
143+
144+ // Evict blocks from the prefix cache by their block IDs (KV-connector-driven).
145+ // DEFERRED 1:1 stub: throws if called.
146+ void evict_blocks (const std::set<int >& block_ids);
126147
127148 // Get num_blocks new blocks from the free block pool. Does NOT check the
128149 // prefix cache. Throws std::runtime_error (upstream ValueError) if there are
@@ -133,6 +154,20 @@ class BlockPool {
133154 // and evict it from the cache. Returns true iff the block was evicted.
134155 bool _maybe_evict_cached_block (KVCacheBlock* block);
135156
157+ // Remove every hash key that points to `block` (its primary hash plus any
158+ // partial-alias hashes in cached_block_hashes_by_block), reset the block's
159+ // hash, and return the keys actually removed from the map. Mirrors upstream
160+ // _remove_cached_block_hashes.
161+ std::vector<BlockHashWithGroupId> _remove_cached_block_hashes (
162+ KVCacheBlock* block);
163+
164+ // Insert a hash key for `block` into cached_block_hash_to_block. If the block
165+ // has no primary hash yet, the key becomes its primary hash (with num_tokens);
166+ // otherwise the key is tracked as a partial alias in
167+ // cached_block_hashes_by_block. Mirrors upstream _insert_block_hash.
168+ void _insert_block_hash (const BlockHashWithGroupId& block_hash_with_group_id,
169+ KVCacheBlock* block, std::optional<int > num_tokens);
170+
136171 // Touch a block: increase its reference count by 1, and remove it from the
137172 // free queue if it was an eviction candidate (ref_cnt == 0). Used when a
138173 // block is hit by another request with the same prefix.
@@ -162,6 +197,7 @@ class BlockPool {
162197
163198 int64_t num_gpu_blocks;
164199 bool enable_caching;
200+ int hash_block_size;
165201
166202 // All kv-cache blocks, indexed by block_id (blocks[0] is the null block).
167203 std::vector<KVCacheBlock> blocks;
@@ -176,6 +212,14 @@ class BlockPool {
176212 std::unordered_map<BlockHashWithGroupId, std::map<int , KVCacheBlock*>>
177213 cached_block_hash_to_block;
178214
215+ // {block_id -> set of partial-alias hash keys}. Tracks the extra hash keys
216+ // (beyond a block's primary block_hash) that point to the block, so eviction /
217+ // reset / promotion can remove every key. Only populated by the deferred
218+ // partial primitives; empty on the common path. Mirrors upstream
219+ // cached_block_hashes_by_block.
220+ std::unordered_map<int , std::set<BlockHashWithGroupId>>
221+ cached_block_hashes_by_block;
222+
179223 // The null placeholder block (block_id 0). Its ref_cnt is NOT maintained;
180224 // it is popped out of the free queue so it can never be allocated.
181225 KVCacheBlock* null_block;
0 commit comments