Skip to content

Commit cadefd2

Browse files
authored
[Performance] DSV4 prefix cache hit rate optimize (vllm-project#11107)
### What this PR does / why we need it? refer to this issue: vllm-project#10970 and vllm-project/vllm#43447 , vllm-project/vllm#44082 , Address the issue of 0% DSV4 prefix cache hit rate, which is caused by turning on MTP ### How was this patch tested? test result: <img width="1644" height="416" alt="image" src="https://github.com/user-attachments/assets/2a7c1d2c-fd4c-439f-8777-91b25c219d36" /> model script: export OMP_PROC_BIND=false export OMP_NUM_THREADS=10 export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD export HCCL_BUFFSIZE=1024 export VLLM_ASCEND_ENABLE_FLASHCOMM1=1 export TASK_QUEUE_ENABLE=1 export HCCL_OP_EXPANSION_MODE="AIV" export VLLM_PREFIX_CACHE_RETENTION_INTERVAL=16384 vllm serve /nas/disk6/DeepSeek-V4-Flash-HW-W8A8/DeepSeek-V4-Flash-HW-W8A8_20260512_02 \ --max_model_len 1048576 \ --max-num-batched-tokens 10240 \ --served-model-name dsv4 \ --gpu-memory-utilization 0.9 \ --api-server-count 1 \ --max-num-seqs 32 \ --data-parallel-size 2 \ --tensor-parallel-size 8 \ --enable-expert-parallel \ --tokenizer-mode deepseek_v4 \ --tool-call-parser deepseek_v4 \ --enable-auto-tool-choice \ --reasoning-parser deepseek_v4 \ --safetensors-load-strategy 'prefetch' \ --enable-prefix-caching \ --model-loader-extra-config='{"enable_multithread_load": "true", "num_threads": 128}' \ --quantization ascend \ --port 8898 \ --block-size 128 \ --speculative-config '{"num_speculative_tokens": 1,"method": "mtp","enforce_eager": true}' \ --async-scheduling \ --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}'\ --additional-config ' {"ascend_compilation_config":{ "enable_npugraph_ex":true, "enable_static_kernel":false }, "enable_cpu_binding": true, "enable_dsa_cp": true, "multistream_overlap_shared_expert":true}' test script: vllm bench serve --backend openai-chat --model dsv4 --served-model-name dsv4 --tokenizer /nas/disk6/DeepSeek-V4-Flash-HW-W8A8/DeepSeek-V4-Flash-HW-W8A8_20260512_02 --percentile-metrics ttft,tpot,itl,e2el --dataset-name random --port 8898 --max-concurrency 10 --num-prompts 40 --endpoint /v1/chat/completions --seed 42 --ignore-eos --random-input-len 48000 --random-output-len 300 --random-range-ratio 0 --random-prefix-len 20000 - vLLM version: v0.23.0 - vLLM main: vllm-project/vllm@967c5c3 Signed-off-by: HF-001 <1670186653@qq.com>
1 parent fd2aa3f commit cadefd2

2 files changed

Lines changed: 100 additions & 0 deletions

File tree

tests/ut/patch/platform/test_prefix_cache_cp_patches.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,84 @@ def _fake_ascend_coordinator(*args, **kwargs):
304304
assert coordinator is sentinel
305305

306306

307+
class _FakeEagleManager:
308+
def __init__(self) -> None:
309+
self.use_eagle = False
310+
311+
312+
def test_verify_and_split_propagates_eagle_to_managers() -> None:
313+
"""Regression for DeepSeek-V4 prefix-cache hit rate 0% with MTP/EAGLE.
314+
315+
The eagle bit must reach each single-type manager: the SWA write path
316+
(``cache_blocks`` -> ``reachable_block_mask``) keys the retained checkpoint
317+
tail on ``manager.use_eagle``, while the read path
318+
(``find_longest_cache_hit``) applies ``drop_eagle_block`` to the same
319+
groups. If the manager keeps the default ``use_eagle=False`` the retained
320+
tail is one block short of the eagle peek boundary, the SWA group never
321+
hits, and the min-over-groups hybrid hit collapses to 0%.
322+
"""
323+
kv_cache_config = _make_deepseek_v4_kv_cache_config()
324+
325+
coordinator = AscendHybridKVCacheCoordinator.__new__(AscendHybridKVCacheCoordinator)
326+
coordinator.kv_cache_config = kv_cache_config
327+
coordinator.dcp_world_size = 1
328+
coordinator.pcp_world_size = 1
329+
coordinator.enable_caching = True
330+
# The c128 group (index 1) carries the EAGLE/MTP layers.
331+
coordinator.eagle_group_ids = {1}
332+
333+
coordinator.single_type_managers = (_FakeEagleManager(), _FakeEagleManager())
334+
335+
coordinator.verify_and_split_kv_cache_groups()
336+
337+
assert coordinator.single_type_managers[1].use_eagle is True
338+
assert coordinator.single_type_managers[0].use_eagle is False
339+
340+
341+
def test_verify_and_split_propagates_eagle_to_merged_spec_siblings() -> None:
342+
"""Upstream ``_annotate_eagle_groups_deepseek_v4`` flags only the single
343+
group holding the MTP layer, but the read path merges same-spec groups and
344+
applies ``drop_eagle_block`` to the whole merged group. So every sibling
345+
sharing that spec must also get ``use_eagle=True`` on the write path, else
346+
``get_cached_block`` (which needs the block cached for *all* group ids)
347+
misses and the hit collapses to 0%.
348+
"""
349+
base_config = _make_deepseek_v4_kv_cache_config()
350+
# Reuse the c128 spec object so the two c128 groups compare equal and merge
351+
# into one attention group in verify_and_split.
352+
c128_group_spec = base_config.kv_cache_groups[1].kv_cache_spec
353+
kv_cache_config = KVCacheConfig(
354+
num_blocks=base_config.num_blocks,
355+
kv_cache_tensors=base_config.kv_cache_tensors,
356+
kv_cache_groups=[
357+
base_config.kv_cache_groups[0], # c4 -> gid 0 (distinct spec)
358+
base_config.kv_cache_groups[1], # c128 -> gid 1
359+
KVCacheGroupSpec(layer_names=["c128_attn_mtp"], kv_cache_spec=c128_group_spec), # gid 2
360+
],
361+
)
362+
363+
coordinator = AscendHybridKVCacheCoordinator.__new__(AscendHybridKVCacheCoordinator)
364+
coordinator.kv_cache_config = kv_cache_config
365+
coordinator.dcp_world_size = 1
366+
coordinator.pcp_world_size = 1
367+
coordinator.enable_caching = True
368+
# Only the MTP sibling (gid 2) is flagged, exactly as upstream does.
369+
coordinator.eagle_group_ids = {2}
370+
371+
coordinator.single_type_managers = (
372+
_FakeEagleManager(),
373+
_FakeEagleManager(),
374+
_FakeEagleManager(),
375+
)
376+
377+
coordinator.verify_and_split_kv_cache_groups()
378+
379+
# Both gid 1 and gid 2 share the c128 spec and merge, so both must be eagle.
380+
assert coordinator.single_type_managers[1].use_eagle is True
381+
assert coordinator.single_type_managers[2].use_eagle is True
382+
assert coordinator.single_type_managers[0].use_eagle is False
383+
384+
307385
def test_deepseek_v4_detection_handles_non_mapping_nested_specs() -> None:
308386
kv_cache_spec = SimpleNamespace(
309387
kv_cache_specs=[

vllm_ascend/patch/platform/patch_kv_cache_coordinator.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,28 @@ def verify_and_split_kv_cache_groups(self) -> None:
197197
if any(gid in self.eagle_group_ids for gid in group_ids)
198198
}
199199

200+
# Propagate the eagle bit to every manager in an eagle-containing
201+
# attention group, mirroring upstream
202+
# HybridKVCacheCoordinator.verify_and_split_kv_cache_groups. Managers
203+
# default to ``use_eagle=False`` ("initialized lazily by the
204+
# coordinator", see SingleTypeKVCacheManager.__init__).
205+
#
206+
# Required for prefix-cache correctness on DeepSeek-V4 + MTP/EAGLE: the
207+
# SWA write path (``cache_blocks`` -> ``reachable_block_mask``) keys the
208+
# retained checkpoint tail on ``manager.use_eagle``, while the read path
209+
# (``find_longest_cache_hit``) applies ``drop_eagle_block`` to every gid
210+
# merged into the eagle attention group (and ``get_cached_block``
211+
# requires the block cached for *all* of them). If any such manager
212+
# keeps the default False, its retained tail ends one block short of the
213+
# eagle "peek" boundary the read looks at, the SWA group never hits, and
214+
# the min-over-groups hybrid hit collapses to 0%. Note the upstream
215+
# ``_annotate_eagle_groups_deepseek_v4`` flags only the single group
216+
# holding the MTP layer, so iterating ``eagle_group_ids`` alone would
217+
# miss its same-spec siblings.
218+
for idx in self.eagle_attn_group_indices:
219+
for gid in self.attention_groups[idx][1]:
220+
self.single_type_managers[gid].use_eagle = True
221+
200222
# The LCM of the block sizes of all attention types.
201223
# The cache hit length must be a multiple of the LCM of the block sizes
202224
# to make sure the cache hit length is a multiple of the block size of

0 commit comments

Comments
 (0)