[2/N][Core] support partial prefix cache hit for hybrid model - #46384
Conversation
Signed-off-by: zjy0516 <riverclouds.zhu@qq.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 55de9c44e1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai> (cherry picked from commit 425b969)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7327f58bea
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb2b1592cd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: zjy0516 <riverclouds.zhu@qq.com>
Signed-off-by: zjy0516 <riverclouds.zhu@qq.com>
### What this PR does / why we need it? Adapt vllm-ascend to vLLM main commits up to July 17. ### Changes | Files | Upstream vLLM change | vllm-ascend adaptation | |-------|---------------------|------------------------| | `vllm_ascend/_310p/ops/fla/idex.py`<br>`vllm_ascend/_310p/ops/fla/l2norm.py`<br>`vllm_ascend/ops/bailing_moe_linear_attn.py`<br>`vllm_ascend/ops/gdn.py`<br>`vllm_ascend/ops/triton/fla/chunk.py`<br>`vllm_ascend/patch/worker/patch_idex_310.py`<br>`vllm_ascend/patch/worker/patch_triton.py`<br>`tests/e2e/nightly/.../test_fused_recurrent_gated_delta_rule.py`<br>`tests/e2e/nightly/.../test_fused_sigmoid_gating_delta_rule.py`<br>`tests/ut/ops/test_gdn_attn_builder.py` | [vllm#48500](vllm-project/vllm#48500) relocated flash-linear-attention from `vllm.third_party.flash_linear_attention` to `vllm.model_executor.layers.fla` | Version-gated all FLA imports and monkey-patch sites with `vllm_version_is("0.25.1")` | | `vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/config_data.py` | [vllm#45939](vllm-project/vllm#45939) replaced SHA-256 rehashed grouped block hashes with chained fine-grained hashes (terminal hash identifies complete block) | Removed `_rehash_block_hash_group` and associated constants. `get_block_hashes` returns `block_hashes[idx + scale_factor - 1]` instead of computing compound hash. | | `vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/coordinator.py`<br>`vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/pool_scheduler.py`<br>`vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/pool_worker.py` | [vllm#45939](vllm-project/vllm#45939) added `hash_block_size` to block pool API; [vllm#46384](vllm-project/vllm#46384) renamed `prefix_match_unit` -> `hash_block_size`, moved hash resolution into individual managers | `ExternalCachedBlockPool` takes `hash_block_size`. `_find_longest_cache_hit` return version-gated. `block_hashes_for_spec` no-op on main. `prefix_match_unit` fallback on main. | | `vllm_ascend/core/recompute_scheduler.py`<br>`vllm_ascend/core/scheduler_profiling_chunk.py`<br>`vllm_ascend/core/single_type_kv_cache_manager.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) changed `get_computed_blocks` 3-tuple -> 2-tuple, `get_num_blocks_to_allocate` params, `find_longest_cache_hit` return | Version-gated unpacking with `cast`. `num_tokens_main_model` made optional. `find_longest_cache_hit` returns blocks-only on v0.25.1 vs `(blocks, hit_length)` on main. | | `vllm_ascend/patch/platform/patch_kv_cache_coordinator.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) restructured return contracts; added partial Mamba hash hit support on main | Added `enable_partial_hash_hits` (main-only). Added `_cache_hit_alignment_tokens`. `find_longest_cache_hit` tracks per-group lengths, uses `cdiv`. `find_longest_cache_hit_per_group` simplified to single-pass; returns per-group `(blocks, lengths)`. Fixed v0.25.1 `hit_length` over-counting: use physical `spec.block_size` (not `_get_effective_block_size()` which includes `compress_ratio`) when multiplying by physical block count — affected both `find_longest_cache_hit` and `find_longest_cache_hit_per_group`; without this, MLA models (DS-V2-Lite/V3/V4) got 4x inflated hit length -> scheduler skipped non-cached tokens -> segfault. | | `vllm_ascend/patch/platform/patch_mamba_manager.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) changed method signatures | Version-gated `find_longest_cache_hit` (delegates to super on main) and `get_num_blocks_to_allocate` (optional params). | | `vllm_ascend/patch/worker/patch_qwen3_5.py` | [vllm#47006](vllm-project/vllm#47006) changed Qwen3-Next sequence-parallel gather contracts | Added `_ascend_all_gather_hidden_and_residual` (main-only). Gated `Qwen3_5DecoderLayer.forward` to v0.25.1. | | `vllm_ascend/ops/vocab_parallel_embedding.py` | [vllm#48390](vllm-project/vllm#48390) changed LM head apply contract | Added `_apply_head` routing to `quant_method.apply` on v0.25.1 and `super()._apply_head` on main. | | `vllm_ascend/patch/worker/__init__.py`<br>`vllm_ascend/patch/worker/patch_v2/patch_eagle_speculator.py`<br>`vllm_ascend/worker/v2/spec_decode/eagle/aclgraph.py`<br>`vllm_ascend/worker/v2/spec_decode/eagle/speculator.py`<br>`vllm_ascend/worker/v2/spec_decode/dflash/speculator.py`<br>`vllm_ascend/worker/v2/spec_decode/dspark/speculator.py` | [vllm#48261](vllm-project/vllm#48261) / [vllm#48167](vllm-project/vllm#48167) unified `PrefillSpeculatorCudaGraphManager` + `DecodeSpeculatorCudaGraphManager` -> `SpeculatorCudaGraphManager`; `capture()` parameterless; `set_attn` new params; `dflash_causal` -> `_group_causal` | Gated MRV2 spec decode imports to main-only. Consolidated Eagle managers into `EagleAclGraphManager`. Simplified DFlash/DSpark `capture()`. Added `target_input_buffers`/`target_attn_groups` to `set_attn`. Renamed `dflash_causal` -> `_group_causal` in DSpark `build_draft_attn_metadatas` (DFlash was already updated; DSpark was missed). | | `tests/ut/ops/test_gdn_layerwise_kv.py`<br>`tests/ut/ops/a2/test_gdn_layerwise_kv.py` | [vllm#46998](vllm-project/vllm#46998) changed GDN `forward` from `(hidden_states, output)` -> `(hidden_states) -> Tensor` | Added `_run_gdn_forward` wrapper handling both calling conventions. | | `tests/e2e/conftest.py` | [vllm#48549](vllm-project/vllm#48549) removed `swap_space` from `LLM` | Removed from `VllmRunner` and `DPVllmRunner`. | | `tests/e2e/pull_request/one_card/model_runner_v2/test_basic.py` | [vllm#48261](vllm-project/vllm#48261) MRV2 spec decode main-only | Added `_SKIP_V025_MRV2_SPEC_DECODE` skip marker. | | `tests/ut/core/test_recompute_scheduler.py` | Upstream main's `_free_request` accesses `self.ec_connector` (new attribute) | Added `scheduler.ec_connector = None` to test setup — scheduler is constructed via `__new__` (bypasses `__init__`), so the attribute must be mocked explicitly. | | `tests/ut/distributed/ascend_store/test_config_data.py`<br>`tests/ut/distributed/ascend_store/test_coordinator.py`<br>`tests/ut/distributed/ascend_store/test_pool_worker.py` | [vllm#45939](vllm-project/vllm#45939) / [vllm#46384](vllm-project/vllm#46384) | Updated block hash assertions to terminal hash. Added `hash_block_size`. Version-gated mock returns. | | `tests/ut/ops/test_vocab_parallel_embedding.py` | [vllm#48390](vllm-project/vllm#48390) | Replaced `patch()` with context manager for `set_current_vllm_config`. | | `tests/ut/patch/platform/test_prefix_cache_cp_patches.py` | [vllm#47782](vllm-project/vllm#47782) | Removed obsolete `num_prompt_tokens` kwarg. | | `tests/ut/patch/worker/test_patch_qwen3_5_mtp.py` | [vllm#48429](vllm-project/vllm#48429) | Set `use_attn_reduce_scatter_for_moe = False` on mock layer. | | `tests/ut/test_compressed_prefix_cache.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) | Version-gated unpacking. Added `test_find_longest_cache_hit_per_group`. | | `vllm_ascend/patch/__init__.py` | - | Updated HunyuanVL comment block. | - vLLM version: v0.25.1 - vLLM main: vllm-project/vllm@54503ec --------- Signed-off-by: wjunLu <wjunlu217@gmail.com> Signed-off-by: hfadzxy <starmoon_zhang@163.com> Co-authored-by: hfadzxy <starmoon_zhang@163.com>
`find_longest_cache_hit` returns `(computed_blocks, hit_length)` since vllm-project#46384 ("[2/N][Core] support partial prefix cache hit for hybrid model"), where `computed_blocks` is a tuple with one block list per KV cache group. `test_mamba_possible_cached_prefix_with_eagle_drop` indexed only `[0]`, so it asserted against the tuple of groups rather than group 0's block list, and failed with `assert 1 == 2`. The other tests in this file already use `[0][0]`. Test-only change; the MambaManager logic is unaffected and its assertions (null_block at index 0, cached block 11 at index 1) pass once the correct list is selected.
`find_longest_cache_hit` returns `(computed_blocks, hit_length)` since vllm-project#46384 ("[2/N][Core] support partial prefix cache hit for hybrid model"), where `computed_blocks` is a tuple with one block list per KV cache group. `test_mamba_possible_cached_prefix_with_eagle_drop` indexed only `[0]`, so it asserted against the tuple of groups rather than group 0's block list, and failed with `assert 1 == 2`. The other tests in this file already use `[0][0]`. Test-only change; the MambaManager logic is unaffected and its assertions (null_block at index 0, cached block 11 at index 1) pass once the correct list is selected.
`find_longest_cache_hit` returns `(computed_blocks, hit_length)` since vllm-project#46384 ("[2/N][Core] support partial prefix cache hit for hybrid model"), where `computed_blocks` is a tuple with one block list per KV cache group. `test_mamba_possible_cached_prefix_with_eagle_drop` indexed only `[0]`, so it asserted against the tuple of groups rather than group 0's block list, and failed with `assert 1 == 2`. The other tests in this file already use `[0][0]`. Test-only change; the MambaManager logic is unaffected and its assertions (null_block at index 0, cached block 11 at index 1) pass once the correct list is selected.
`find_longest_cache_hit` returns `(computed_blocks, hit_length)` since vllm-project#46384 ("[2/N][Core] support partial prefix cache hit for hybrid model"), where `computed_blocks` is a tuple with one block list per KV cache group. `test_mamba_possible_cached_prefix_with_eagle_drop` indexed only `[0]`, so it asserted against the tuple of groups rather than group 0's block list, and failed with `assert 1 == 2`. The other tests in this file already use `[0][0]`. Test-only change; the MambaManager logic is unaffected and its assertions (null_block at index 0, cached block 11 at index 1) pass once the correct list is selected.
`find_longest_cache_hit` returns `(computed_blocks, hit_length)` since vllm-project#46384 ("[2/N][Core] support partial prefix cache hit for hybrid model"), where `computed_blocks` is a tuple with one block list per KV cache group. `test_mamba_possible_cached_prefix_with_eagle_drop` indexed only `[0]`, so it asserted against the tuple of groups rather than group 0's block list, and failed with `assert 1 == 2`. The other tests in this file already use `[0][0]`. Test-only change; the MambaManager logic is unaffected and its assertions (null_block at index 0, cached block 11 at index 1) pass once the correct list is selected.
…roject#46384) Signed-off-by: zjy0516 <riverclouds.zhu@qq.com> Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai> Co-authored-by: Yifan Qiao <yifanqiao@inferact.ai> Signed-off-by: root <root@smci355-ccs-aus-m02-09.cs-aus.dcgpu>
Thanks again for the partial-prefix-cache work. We integrated the approach and confirmed that it enables partial cache reuse for aligned full-attention + Mamba hybrid models. However, we still observe a relatively low prefix-cache hit rate on our real workload. Our understanding is that each request retains the final
the cache can retain a checkpoint around token This is problematic for our workload because many requests share a common system/context prefix near the beginning, then diverge with different user content: |
…roject#46384) Signed-off-by: zjy0516 <riverclouds.zhu@qq.com> Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai> Co-authored-by: Yifan Qiao <yifanqiao@inferact.ai> (cherry picked from commit 481e481) (cherry picked from commit a0ad76a)
### What this PR does / why we need it? Adapt vllm-ascend to vLLM main commits up to July 17. ### Changes | Files | Upstream vLLM change | vllm-ascend adaptation | |-------|---------------------|------------------------| | `vllm_ascend/_310p/ops/fla/idex.py`<br>`vllm_ascend/_310p/ops/fla/l2norm.py`<br>`vllm_ascend/ops/bailing_moe_linear_attn.py`<br>`vllm_ascend/ops/gdn.py`<br>`vllm_ascend/ops/triton/fla/chunk.py`<br>`vllm_ascend/patch/worker/patch_idex_310.py`<br>`vllm_ascend/patch/worker/patch_triton.py`<br>`tests/e2e/nightly/.../test_fused_recurrent_gated_delta_rule.py`<br>`tests/e2e/nightly/.../test_fused_sigmoid_gating_delta_rule.py`<br>`tests/ut/ops/test_gdn_attn_builder.py` | [vllm#48500](vllm-project/vllm#48500) relocated flash-linear-attention from `vllm.third_party.flash_linear_attention` to `vllm.model_executor.layers.fla` | Version-gated all FLA imports and monkey-patch sites with `vllm_version_is("0.25.1")` | | `vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/config_data.py` | [vllm#45939](vllm-project/vllm#45939) replaced SHA-256 rehashed grouped block hashes with chained fine-grained hashes (terminal hash identifies complete block) | Removed `_rehash_block_hash_group` and associated constants. `get_block_hashes` returns `block_hashes[idx + scale_factor - 1]` instead of computing compound hash. | | `vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/coordinator.py`<br>`vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/pool_scheduler.py`<br>`vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/pool_worker.py` | [vllm#45939](vllm-project/vllm#45939) added `hash_block_size` to block pool API; [vllm#46384](vllm-project/vllm#46384) renamed `prefix_match_unit` -> `hash_block_size`, moved hash resolution into individual managers | `ExternalCachedBlockPool` takes `hash_block_size`. `_find_longest_cache_hit` return version-gated. `block_hashes_for_spec` no-op on main. `prefix_match_unit` fallback on main. | | `vllm_ascend/core/recompute_scheduler.py`<br>`vllm_ascend/core/scheduler_profiling_chunk.py`<br>`vllm_ascend/core/single_type_kv_cache_manager.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) changed `get_computed_blocks` 3-tuple -> 2-tuple, `get_num_blocks_to_allocate` params, `find_longest_cache_hit` return | Version-gated unpacking with `cast`. `num_tokens_main_model` made optional. `find_longest_cache_hit` returns blocks-only on v0.25.1 vs `(blocks, hit_length)` on main. | | `vllm_ascend/patch/platform/patch_kv_cache_coordinator.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) restructured return contracts; added partial Mamba hash hit support on main | Added `enable_partial_hash_hits` (main-only). Added `_cache_hit_alignment_tokens`. `find_longest_cache_hit` tracks per-group lengths, uses `cdiv`. `find_longest_cache_hit_per_group` simplified to single-pass; returns per-group `(blocks, lengths)`. Fixed v0.25.1 `hit_length` over-counting: use physical `spec.block_size` (not `_get_effective_block_size()` which includes `compress_ratio`) when multiplying by physical block count — affected both `find_longest_cache_hit` and `find_longest_cache_hit_per_group`; without this, MLA models (DS-V2-Lite/V3/V4) got 4x inflated hit length -> scheduler skipped non-cached tokens -> segfault. | | `vllm_ascend/patch/platform/patch_mamba_manager.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) changed method signatures | Version-gated `find_longest_cache_hit` (delegates to super on main) and `get_num_blocks_to_allocate` (optional params). | | `vllm_ascend/patch/worker/patch_qwen3_5.py` | [vllm#47006](vllm-project/vllm#47006) changed Qwen3-Next sequence-parallel gather contracts | Added `_ascend_all_gather_hidden_and_residual` (main-only). Gated `Qwen3_5DecoderLayer.forward` to v0.25.1. | | `vllm_ascend/ops/vocab_parallel_embedding.py` | [vllm#48390](vllm-project/vllm#48390) changed LM head apply contract | Added `_apply_head` routing to `quant_method.apply` on v0.25.1 and `super()._apply_head` on main. | | `vllm_ascend/patch/worker/__init__.py`<br>`vllm_ascend/patch/worker/patch_v2/patch_eagle_speculator.py`<br>`vllm_ascend/worker/v2/spec_decode/eagle/aclgraph.py`<br>`vllm_ascend/worker/v2/spec_decode/eagle/speculator.py`<br>`vllm_ascend/worker/v2/spec_decode/dflash/speculator.py`<br>`vllm_ascend/worker/v2/spec_decode/dspark/speculator.py` | [vllm#48261](vllm-project/vllm#48261) / [vllm#48167](vllm-project/vllm#48167) unified `PrefillSpeculatorCudaGraphManager` + `DecodeSpeculatorCudaGraphManager` -> `SpeculatorCudaGraphManager`; `capture()` parameterless; `set_attn` new params; `dflash_causal` -> `_group_causal` | Gated MRV2 spec decode imports to main-only. Consolidated Eagle managers into `EagleAclGraphManager`. Simplified DFlash/DSpark `capture()`. Added `target_input_buffers`/`target_attn_groups` to `set_attn`. Renamed `dflash_causal` -> `_group_causal` in DSpark `build_draft_attn_metadatas` (DFlash was already updated; DSpark was missed). | | `tests/ut/ops/test_gdn_layerwise_kv.py`<br>`tests/ut/ops/a2/test_gdn_layerwise_kv.py` | [vllm#46998](vllm-project/vllm#46998) changed GDN `forward` from `(hidden_states, output)` -> `(hidden_states) -> Tensor` | Added `_run_gdn_forward` wrapper handling both calling conventions. | | `tests/e2e/conftest.py` | [vllm#48549](vllm-project/vllm#48549) removed `swap_space` from `LLM` | Removed from `VllmRunner` and `DPVllmRunner`. | | `tests/e2e/pull_request/one_card/model_runner_v2/test_basic.py` | [vllm#48261](vllm-project/vllm#48261) MRV2 spec decode main-only | Added `_SKIP_V025_MRV2_SPEC_DECODE` skip marker. | | `tests/ut/core/test_recompute_scheduler.py` | Upstream main's `_free_request` accesses `self.ec_connector` (new attribute) | Added `scheduler.ec_connector = None` to test setup — scheduler is constructed via `__new__` (bypasses `__init__`), so the attribute must be mocked explicitly. | | `tests/ut/distributed/ascend_store/test_config_data.py`<br>`tests/ut/distributed/ascend_store/test_coordinator.py`<br>`tests/ut/distributed/ascend_store/test_pool_worker.py` | [vllm#45939](vllm-project/vllm#45939) / [vllm#46384](vllm-project/vllm#46384) | Updated block hash assertions to terminal hash. Added `hash_block_size`. Version-gated mock returns. | | `tests/ut/ops/test_vocab_parallel_embedding.py` | [vllm#48390](vllm-project/vllm#48390) | Replaced `patch()` with context manager for `set_current_vllm_config`. | | `tests/ut/patch/platform/test_prefix_cache_cp_patches.py` | [vllm#47782](vllm-project/vllm#47782) | Removed obsolete `num_prompt_tokens` kwarg. | | `tests/ut/patch/worker/test_patch_qwen3_5_mtp.py` | [vllm#48429](vllm-project/vllm#48429) | Set `use_attn_reduce_scatter_for_moe = False` on mock layer. | | `tests/ut/test_compressed_prefix_cache.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) | Version-gated unpacking. Added `test_find_longest_cache_hit_per_group`. | | `vllm_ascend/patch/__init__.py` | - | Updated HunyuanVL comment block. | - vLLM version: v0.25.1 - vLLM main: vllm-project/vllm@54503ec --------- Signed-off-by: wjunLu <wjunlu217@gmail.com> Signed-off-by: hfadzxy <starmoon_zhang@163.com> Co-authored-by: hfadzxy <starmoon_zhang@163.com>
### What this PR does / why we need it? Adapt vllm-ascend to vLLM main commits up to July 17. ### Changes | Files | Upstream vLLM change | vllm-ascend adaptation | |-------|---------------------|------------------------| | `vllm_ascend/_310p/ops/fla/idex.py`<br>`vllm_ascend/_310p/ops/fla/l2norm.py`<br>`vllm_ascend/ops/bailing_moe_linear_attn.py`<br>`vllm_ascend/ops/gdn.py`<br>`vllm_ascend/ops/triton/fla/chunk.py`<br>`vllm_ascend/patch/worker/patch_idex_310.py`<br>`vllm_ascend/patch/worker/patch_triton.py`<br>`tests/e2e/nightly/.../test_fused_recurrent_gated_delta_rule.py`<br>`tests/e2e/nightly/.../test_fused_sigmoid_gating_delta_rule.py`<br>`tests/ut/ops/test_gdn_attn_builder.py` | [vllm#48500](vllm-project/vllm#48500) relocated flash-linear-attention from `vllm.third_party.flash_linear_attention` to `vllm.model_executor.layers.fla` | Version-gated all FLA imports and monkey-patch sites with `vllm_version_is("0.25.1")` | | `vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/config_data.py` | [vllm#45939](vllm-project/vllm#45939) replaced SHA-256 rehashed grouped block hashes with chained fine-grained hashes (terminal hash identifies complete block) | Removed `_rehash_block_hash_group` and associated constants. `get_block_hashes` returns `block_hashes[idx + scale_factor - 1]` instead of computing compound hash. | | `vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/coordinator.py`<br>`vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/pool_scheduler.py`<br>`vllm_ascend/distributed/kv_transfer/kv_pool/ascend_store/pool_worker.py` | [vllm#45939](vllm-project/vllm#45939) added `hash_block_size` to block pool API; [vllm#46384](vllm-project/vllm#46384) renamed `prefix_match_unit` -> `hash_block_size`, moved hash resolution into individual managers | `ExternalCachedBlockPool` takes `hash_block_size`. `_find_longest_cache_hit` return version-gated. `block_hashes_for_spec` no-op on main. `prefix_match_unit` fallback on main. | | `vllm_ascend/core/recompute_scheduler.py`<br>`vllm_ascend/core/scheduler_profiling_chunk.py`<br>`vllm_ascend/core/single_type_kv_cache_manager.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) changed `get_computed_blocks` 3-tuple -> 2-tuple, `get_num_blocks_to_allocate` params, `find_longest_cache_hit` return | Version-gated unpacking with `cast`. `num_tokens_main_model` made optional. `find_longest_cache_hit` returns blocks-only on v0.25.1 vs `(blocks, hit_length)` on main. | | `vllm_ascend/patch/platform/patch_kv_cache_coordinator.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) restructured return contracts; added partial Mamba hash hit support on main | Added `enable_partial_hash_hits` (main-only). Added `_cache_hit_alignment_tokens`. `find_longest_cache_hit` tracks per-group lengths, uses `cdiv`. `find_longest_cache_hit_per_group` simplified to single-pass; returns per-group `(blocks, lengths)`. Fixed v0.25.1 `hit_length` over-counting: use physical `spec.block_size` (not `_get_effective_block_size()` which includes `compress_ratio`) when multiplying by physical block count — affected both `find_longest_cache_hit` and `find_longest_cache_hit_per_group`; without this, MLA models (DS-V2-Lite/V3/V4) got 4x inflated hit length -> scheduler skipped non-cached tokens -> segfault. | | `vllm_ascend/patch/platform/patch_mamba_manager.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) changed method signatures | Version-gated `find_longest_cache_hit` (delegates to super on main) and `get_num_blocks_to_allocate` (optional params). | | `vllm_ascend/patch/worker/patch_qwen3_5.py` | [vllm#47006](vllm-project/vllm#47006) changed Qwen3-Next sequence-parallel gather contracts | Added `_ascend_all_gather_hidden_and_residual` (main-only). Gated `Qwen3_5DecoderLayer.forward` to v0.25.1. | | `vllm_ascend/ops/vocab_parallel_embedding.py` | [vllm#48390](vllm-project/vllm#48390) changed LM head apply contract | Added `_apply_head` routing to `quant_method.apply` on v0.25.1 and `super()._apply_head` on main. | | `vllm_ascend/patch/worker/__init__.py`<br>`vllm_ascend/patch/worker/patch_v2/patch_eagle_speculator.py`<br>`vllm_ascend/worker/v2/spec_decode/eagle/aclgraph.py`<br>`vllm_ascend/worker/v2/spec_decode/eagle/speculator.py`<br>`vllm_ascend/worker/v2/spec_decode/dflash/speculator.py`<br>`vllm_ascend/worker/v2/spec_decode/dspark/speculator.py` | [vllm#48261](vllm-project/vllm#48261) / [vllm#48167](vllm-project/vllm#48167) unified `PrefillSpeculatorCudaGraphManager` + `DecodeSpeculatorCudaGraphManager` -> `SpeculatorCudaGraphManager`; `capture()` parameterless; `set_attn` new params; `dflash_causal` -> `_group_causal` | Gated MRV2 spec decode imports to main-only. Consolidated Eagle managers into `EagleAclGraphManager`. Simplified DFlash/DSpark `capture()`. Added `target_input_buffers`/`target_attn_groups` to `set_attn`. Renamed `dflash_causal` -> `_group_causal` in DSpark `build_draft_attn_metadatas` (DFlash was already updated; DSpark was missed). | | `tests/ut/ops/test_gdn_layerwise_kv.py`<br>`tests/ut/ops/a2/test_gdn_layerwise_kv.py` | [vllm#46998](vllm-project/vllm#46998) changed GDN `forward` from `(hidden_states, output)` -> `(hidden_states) -> Tensor` | Added `_run_gdn_forward` wrapper handling both calling conventions. | | `tests/e2e/conftest.py` | [vllm#48549](vllm-project/vllm#48549) removed `swap_space` from `LLM` | Removed from `VllmRunner` and `DPVllmRunner`. | | `tests/e2e/pull_request/one_card/model_runner_v2/test_basic.py` | [vllm#48261](vllm-project/vllm#48261) MRV2 spec decode main-only | Added `_SKIP_V025_MRV2_SPEC_DECODE` skip marker. | | `tests/ut/core/test_recompute_scheduler.py` | Upstream main's `_free_request` accesses `self.ec_connector` (new attribute) | Added `scheduler.ec_connector = None` to test setup — scheduler is constructed via `__new__` (bypasses `__init__`), so the attribute must be mocked explicitly. | | `tests/ut/distributed/ascend_store/test_config_data.py`<br>`tests/ut/distributed/ascend_store/test_coordinator.py`<br>`tests/ut/distributed/ascend_store/test_pool_worker.py` | [vllm#45939](vllm-project/vllm#45939) / [vllm#46384](vllm-project/vllm#46384) | Updated block hash assertions to terminal hash. Added `hash_block_size`. Version-gated mock returns. | | `tests/ut/ops/test_vocab_parallel_embedding.py` | [vllm#48390](vllm-project/vllm#48390) | Replaced `patch()` with context manager for `set_current_vllm_config`. | | `tests/ut/patch/platform/test_prefix_cache_cp_patches.py` | [vllm#47782](vllm-project/vllm#47782) | Removed obsolete `num_prompt_tokens` kwarg. | | `tests/ut/patch/worker/test_patch_qwen3_5_mtp.py` | [vllm#48429](vllm-project/vllm#48429) | Set `use_attn_reduce_scatter_for_moe = False` on mock layer. | | `tests/ut/test_compressed_prefix_cache.py` | [vllm#46384](vllm-project/vllm#46384) / [vllm#47782](vllm-project/vllm#47782) | Version-gated unpacking. Added `test_find_longest_cache_hit_per_group`. | | `vllm_ascend/patch/__init__.py` | - | Updated HunyuanVL comment block. | - vLLM version: v0.25.1 - vLLM main: vllm-project/vllm@54503ec --------- Signed-off-by: wjunLu <wjunlu217@gmail.com> Signed-off-by: hfadzxy <starmoon_zhang@163.com> Co-authored-by: hfadzxy <starmoon_zhang@163.com>
Purpose
This PR builds on the partial prefix-cache primitives from #45939 and completes fine-grained prefix-cache hits for aligned full-attention + Mamba hybrid models. The overall design is described in RFC #45702.
The PR adds:
prefix_match_unitthat selects prefix-match granularity independently from physical KV-cache block sizes;It e2e works now
Use 2 sequential request to simulate multi-turn:
Design
Match unit and cache-manager hash views
Request.block_hashesis computed atprefix_match_unitgranularity. Each KV-cache group continues to allocate its existing physical block size.For example:
Managers using physical-block lookup receive a grouped view such as
[H4, H8, ...]. Managers supporting fine-grained lookup keep the original hash list and may probeH2orH6inside a physical block.The configured match unit must divide every cache-group block size. If it is not specified, vLLM derives it from the GCD of the resolved group block sizes.
Explicit token-length cache hits
Cache-manager lookup now returns both the physical blocks and the exact number of matched tokens:
The separate length is required because a hit at
H6includes the physical block[4, 8), but only tokens[4, 6)in that block are valid.FullAttentionManagerfirst matches complete physical blocks from the start of the request, then probes hash boundaries inside the next block from longest to shortest.MambaManagerkeeps its existing right-to-left state lookup, but can now search at match-unit boundaries and return the physical block containing the matched recurrent state.Managers without fine-grained support continue to return block-aligned hit lengths through the same API.
Hybrid-coordinator convergence
The hybrid coordinator tracks a token hit length independently from each group's block list. It repeatedly reduces the candidate length until every cache group can cover the same logical prefix.
For example:
Full-attention results are downward-closed, so an existing result can be trimmed without another lookup. Physical block lists are trimmed with ceiling division so the block containing a partial tail is retained.
EAGLE also uses the explicit token length. In fine-grained mode it drops one match unit, rather than one physical block, before the result is returned.
The explicit-length contract is also propagated to the Mooncake store coordinator. Its block-pool adapter exposes
hash_block_sizeso it can reuse the same manager lookup interface.Creating partial tail entries
Full attention registers the final hash-aligned prompt boundary once the containing physical block is available.
Aligned Mamba requires the recurrent state from exactly that boundary. The scheduler therefore stops once at the final match-unit boundary before the end of a partial prompt block.
MambaManagerregisters that state only when the step lands on this boundary; it does not retain every intermediate state inside the physical block.When execution resumes from a partial hit, the first chunk stops at the next physical block boundary. Subsequent chunks return to the existing block-aligned scheduling path.
Copy-on-write
A request cannot append to a physical block while the prefix cache exposes an entry for an earlier boundary in that block. This PR uses copy-on-write, with two allocation paths.
For a new request hitting a partial entry, the request is redirected to a private block:
The shared cached block remains unchanged and the request continues from the private copy.
An already-running aligned-Mamba request may register a partial entry for its current state and then continue in the next step. Its worker block table is append-only, so that request must remain on the original block. Instead, the cache entry is moved to a new block:
BlockPool.move_block_hashes()re-points all hashes without emitting a remove/store event. The destination is excluded from same-step lookup until the copy has populated it.Copy execution and lifetime
Managers queue
(source, destination)block pairs.KVCacheManagerconverts them to block-ID copies onSchedulerOutput, and workers execute them after zeroing newly allocated blocks and before model forward:The worker copies complete block-major storage pages. Storage aliases across layer views are deduplicated, so each backing allocation is copied once.
Both copy endpoints are retained until the non-empty scheduler step carrying the copy has completed. Their release uses the existing deferred-free queue, preventing either block from being recycled while asynchronous execution is still reading or writing it.
Essential Elements of an Effective PR Description Checklist
supported_models.mdandexamplesfor a new model.