A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 11, 00:00 UTC
[KVEvent] User request.block_hash for parent block_hash (#30544)
Signed-off-by: Chen Zhang <zhangch99@outlook.com> Signed-off-by: Yifan Qiao <yifanqiao@berkeley.edu> Co-authored-by: Yifan Qiao <yifanqiao@berkeley.edu>
Chen Zhang committed
Dec 23, 2025 at 18:23 UTC
538e830caab8d0e7c2557adb975dca3c5af296be
2 files changed
+64
-3
tests/v1/core/test_prefix_caching.py
+63
@@ -1356,6 +1356,69 @@ def test_kv_cache_events(blocks_to_cache: int):
1356
assert len(manager.block_pool.cached_block_hash_to_block) == 0
1357
1358
1359
+def test_null_parent_block_hash():
1360
+ block_size = 1
1361
+ num_cached_blocks = 2
1362
+ num_full_blocks = 4
1363
+
1364
+ pool = BlockPool(
1365
+ num_gpu_blocks=8,
1366
+ enable_caching=True,
1367
+ hash_block_size=block_size,
1368
+ enable_kv_cache_events=True,
1369
+ )
1370
+
1371
+ req = make_request(
1372
+ "req_null_parent",
1373
+ prompt_token_ids=[10, 11, 12, 13],
1374
+ block_size=block_size,
1375
+ hash_fn=sha256,
1376
+ )
1377
+ assert len(req.block_hashes) == num_full_blocks
1378
+
1379
+ # Physical parent is `null_block` (no hash), while the logical parent hash
1380
+ # still exists in `request.block_hashes[num_cached_blocks - 1]`.
1381
+ assert pool.null_block.block_hash is None
1382
+ new_blocks = pool.get_new_blocks(num_full_blocks - 1)
1383
+ blocks = [
1384
+ new_blocks[: num_cached_blocks - 1],
1385
+ pool.null_block, # physical parent
1386
+ *new_blocks[num_cached_blocks - 1 :],
1387
+ ]
1388
+
1389
+ pool.cache_full_blocks(
1390
+ request=req,
1391
+ blocks=blocks,
1392
+ num_cached_blocks=num_cached_blocks,
1393
+ num_full_blocks=num_full_blocks,
1394
+ block_size=block_size,
1395
+ kv_cache_group_id=0,
1396
+ )
1397
+
1398
+ events = pool.take_events()
1399
+ assert len(events) == 1
1400
+ event = events[0]
1401
+ assert isinstance(event, BlockStored)
1402
+
1403
+ expected_parent = kv_cache_utils.maybe_convert_block_hash(
1404
+ req.block_hashes[num_cached_blocks - 1]
1405
+ )
1406
+ assert event.parent_block_hash == expected_parent
1407
+ assert event.parent_block_hash is not None
1408
+
1409
+ expected_new_hashes = [
1410
+ kv_cache_utils.maybe_convert_block_hash(h)
1411
+ for h in req.block_hashes[num_cached_blocks:num_full_blocks]
1412
+ ]
1413
+ assert event.block_hashes == expected_new_hashes
1414
+
1415
+ # Ensure we didn't accidentally assign a hash to the null block.
1416
+ assert pool.null_block.block_hash is None
1417
+ # Sanity check: newly cached physical blocks should have hashes assigned.
1418
+ assert blocks[num_cached_blocks].block_hash is not None
1419
+ assert blocks[num_full_blocks - 1].block_hash is not None
1420
+
1421
+
1422
@pytest.mark.parametrize("blocks_to_cache", [2, 3, 10])
1423
def test_kv_cache_events_with_lora(blocks_to_cache: int):
1424
"""Test BlockStored events contain correct lora_id when using LoRA requests."""
vllm/v1/core/block_pool.py
+1
-3
@@ -270,10 +270,8 @@ class BlockPool:
270
if num_cached_blocks == 0:
271
parent_block_hash: ExternalBlockHash | None = None
272
else:
273
- parent_block = blocks[num_cached_blocks - 1]
274
- assert parent_block.block_hash is not None
273
parent_block_hash = maybe_convert_block_hash(
276
- get_block_hash(parent_block.block_hash)
274
+ block_hashes[num_cached_blocks - 1]
275
)
276
277
self.kv_event_queue.append(