Prhub

#37898 [Hybrid] Marconi-style admission policy for hybrid cache

原始 PR 作者 s3woz 合并时间 2026-06-11 01:03 文件变更 3 提交数 18 评论 33 代码增减 +89 / -0

执行摘要

为混合缓存实现 Marconi 式共享前缀准入策略,提升 Qwen 等模型缓存命中率

Marconi 论文 (https://arxiv.org/abs/2411.19379) 提出两种有效缓存准入策略:最后状态和共享前缀。vLLM 此前只支持最后状态缓存,缺少共享前缀缓存,导致系统提示等重复前缀场景无法充分利用前缀缓存。本 PR 实现共享前缀缓存,以进一步减少预填充时间。

值得精读。该 PR 展示了如何在不修改 kernel 的情况下利用现有缓存机制实现高级缓存准入策略,设计思路可推广到其他 hybrid attention 模型。

讨论亮点

命名讨论

tdoublep 建议将参数名 mamba_tokens_lag 改为 num_uncached_common_prefix_tokens,语义更清晰,已采纳。

未初始化变量风险

gemini-code-assist 指出 mamba_tokens_lag 可能未定义导致 UnboundLocalError;作者说明该路径受条件保护,后续版本通过属性方式彻底规避。

变量复用可读性

gemini-code-assist 指出 num_new_local_computed_tokens 复用易混淆;作者重构为直接使用协调器属性,消除复用。

协调器返回值设计

tdoublep 和 yannicks1 讨论 get_computed_blocks 是否应返回两个长度;最终采用协调器属性方式,简化接口。

条件判断粒度

yannicks1 建议使用 need_mamba_block_aligned_split;作者认为 has_mamba_layers 更通用,以覆盖非 align 模式。

实现拆解

  1. KVCacheCoordinator._get_cache_hit_blocks 中新增 longest_hit_length 记录所有 attention 组的最长缓存命中,计算 num_uncached_common_prefix_tokens 并暴露为属性。
  2. Scheduler.schedule 中,若模型包含 Mamba 层,从协调器读取 num_uncached_common_prefix_tokens
  3. Scheduler._mamba_block_aligned_split 中接收该参数,若未缓存公共前缀 ≥ block_size 且当前调度长度更长,则截断调度长度以强制缓存该前缀,保持块对齐。
  4. 新增测试 test_hybrid_cache_mamba_align_shared_prefix_detection,覆盖共享前缀检测及调度调整逻辑。
文件 模块 状态 重要度
vllm/v1/core/sched/scheduler.py 调度器 modified 6.53
vllm/v1/core/kv_cache_coordinator.py 缓存协调器 modified 5.66
tests/v1/core/test_prefix_caching.py 测试 modified 6.42

关键符号

_mamba_block_aligned_split _get_cache_hit_blocks test_hybrid_cache_mamba_align_shared_prefix_detection

关键源码片段

vllm/v1/core/kv_cache_coordinator.py core-logic

缓存协调器,追踪最长缓存命中长度并暴露未缓存公共前缀长度,为调度器提供决策依据

def _get_cache_hit_blocks(self, block_hashes, max_cache_hit_length):
    # ... 初始化变量 ...
    longest_hit_length = 0 # 新增:记录所有 attention 组中的最长命中
    while True:
        curr_hit_length = hit_length
        for idx, (spec, group_ids, manager_cls, use_eagle) in enumerate(
            self.attention_groups
        ):
            # ... 原有逻辑:查找每组最长缓存命中 ...
            hit_blocks = manager_cls.find_longest_cache_hit(...)
            _new_hit_length = len(hit_blocks[0]) * spec.block_size
            # ... 更新 curr_hit_length ...
            # 新增:更新全局最长命中
            longest_hit_length = max(longest_hit_length, curr_hit_length)
        # ... 循环终止条件 ...
    # ... 截断 Full Attention 块 ...
    # 新增:计算未缓存的公共前缀 token 数
    self.num_uncached_common_prefix_tokens = longest_hit_length - hit_length
    return (blocks_tuple, hit_length)

评论区精华

重命名参数 mamba_tokens_lag 设计

tdoublep 建议将参数名从 mamba_tokens_lag 改为 num_uncached_common_prefix_tokens,以更好地反映语义。

结论:作者同意并修改。 · 已解决

未初始化变量风险 正确性

gemini-code-assist 和 depthfirst-app 指出 mamba_tokens_lag 可能未定义,导致 UnboundLocalError。

结论:作者解释该代码块受条件保护,后续版本通过属性方式避免。 · 已解决

变量复用导致可读性差 style

gemini-code-assist 指出 num_new_local_computed_tokens 变量被复用,含义不清晰。

结论:作者重构为直接返回 num_uncached_common_prefix_tokens,移除了复用。 · 已解决

协调器返回值设计 设计

tdoublep 和 yannicks1 讨论 get_computed_blocks 是否应返回两个长度(当前命中 + 最长命中)。最终采用属性方法简化。

结论:作者改为在协调器上设置属性 num_uncached_common_prefix_tokens,调度器直接读取。 · 已解决

条件判断使用 has_mamba_layers vs need_mamba_block_aligned_split 设计

yannicks1 建议在调度器中使用 need_mamba_block_aligned_split 更精确地触发逻辑。

结论:作者解释 has_mamba_layers 更通用,因为需要获取最长命中长度,即使 mamba 模式不是 align。 · 已解决

测试中断言顺序 测试

yannicks1 建议调整断言顺序,先判断 num_new_tokens。

结论:作者调整了测试中的断言顺序。 · 已解决

风险与影响

  1. 变量未初始化:虽当前路径受保护,但未来条件变更可能导致 UnboundLocalError,最终版本通过属性固化。
  2. 依赖 block_size 对齐:优化仅在 num_uncached_common_prefix_tokens >= block_size 时生效,小前缀无法获益,逻辑正确。
  3. 协调器类型假设:调度器通过 has_mamba_layers 间接判断协调器类型,若引入非 Mamba 混合模型需调整。
  4. 测试覆盖:新增测试覆盖主要场景,但边缘情况(如零前缀、非对齐边界)未充分测试。

用户:使用混合缓存的模型(如 Qwen)在启用 Prefix Caching 后预填充延迟降低 28%~40%。
系统:仅在模型含 Mamba 层且缓存模式为 align 时触发,不影响其他路径。
团队:需维护协调器属性及调度器逻辑,但代码量小,易于理解。

变量未初始化风险 依赖 block_size 对齐 协调器类型假设

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论