Prhub

#48425 [BugFix] Handle per-group prefix-hit divergence for hybrid models with KV connector

原始 PR 作者 njhill 合并时间 2026-07-23 08:19 文件变更 4 提交数 7 评论 8 代码增减 +279 / -40

执行摘要

修复混合模型 KV connector 的 prefix-hit 发散 bug

修复 Issue #46453:Hybrid Mamba + KV connector 模型在块压力下 per-group prefix hit 发散,导致 vllm engine 崩溃。全注意力组的缓存块可能被驱逐而 Mamba 状态保留,或反之,使得 max(per_group_hits) 报告不一致的边界,而 Mamba 状态的传输依赖于连接器提供的 token,若发散未被处理会导致无有效 Mamba 状态。

值得精读的 bugfix,展示了混合缓存组不一致性的检测与修复模式,以及如何通过 fallback 保证状态有效。对于理解 v1 调度器和 KV cache 管理交互有参考价值。设计决策(将发散逻辑移至 KVCacheManager)体现了良好的关注点分离。

讨论亮点
  • ivanium 建议提取逻辑:在 review 中指出“将发散逻辑放在 Scheduler 中感觉过于侵入,应该放到 KVCacheManager 中”。作者接受并实现,在后续提交中将逻辑抽取到 kv_cache_manager.py 的新方法 get_computed_blocks_for_connector
  • howarlii 指出 FA deeper 方向未处理:在 Issue 评论中指出初始版本只处理了 Mamba hit deeper than FA 的方向,遗漏了 FA hit deeper than Mamba 且连接器无外部 token 的场景,建议回退到 get_computed_blocks。作者在第二次提交中增加了 hit_divergednum_external_computed_tokens == 0 的组合判断,howarlii 确认“handles both divergence directions now,LGTM”。

实现拆解

  1. 在 HybridKVCacheCoordinator 中新增 full_attention_group_id:在 verify_and_split_kv_cache_groups 末尾,将第一个全注意力组的索引记录为 self.full_attention_group_id,作为判断发散方向的参考组。
  2. 在 KVCacheManager 中新增 get_computed_blocks_for_connector 方法:先检测是否混合模型、是否存在全注意力组;若是,调用 find_longest_cache_hit_per_group 获取各组命中长度。若任何组命中长度大于全注意力组,说明全注意力块被驱逐,直接回退到 get_computed_blocks;否则以全注意力组命中长度为本地前缀,并返回 hit_diverged 标志(当存在组命中小于全注意力组时置 True)。
  3. 修改 Scheduler.schedule 中的前缀查找分支:将原来内联的混合模型展开逻辑替换为调用 get_computed_blocks_for_connector。当 hit_divergednum_external_computed_tokens == 0(连接器无外部 token)时,回退调用 get_computed_blocks 取得各组一致边界,确保 Mamba 状态有效。同时删除了对 HybridKVCacheCoordinator 的显式 import。
  4. 新增单元测试:在 tests/v1/core/test_scheduler.py 中添加 _create_hybrid_mamba_connector_scheduler 辅助函数和两个参数化测试——test_hybrid_per_group_hit_divergence_with_connector(覆盖 Mamba deeper than FA 场景)和 test_hybrid_per_group_hit_divergence_fa_deeper_no_external(覆盖 FA deeper 且无外部 token 的回退场景)。
文件 模块 状态 重要度
tests/v1/core/test_scheduler.py 调度器 modified 7.14
vllm/v1/core/kv_cache_manager.py 缓存层 modified 7.64
vllm/v1/core/sched/scheduler.py 调度器 modified 7.18
vllm/v1/core/kv_cache_coordinator.py 缓存层 modified 6.09

关键符号

get_computed_blocks_for_connector get_computed_blocks find_longest_cache_hit_per_group schedule

关键源码片段

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

核心变更:新增 `get_computed_blocks_for_connector` 方法,实现 per-group 发散检测与 hit_diverged 标志返回,是修复的关键逻辑。

def get_computed_blocks_for_connector(
    self, request: Request
) -> tuple[KVCacheBlocks, int, int, bool]:
    """
    本地前缀缓存查找,用于带 KV connector 的请求。
    处理混合模型中 per-group 命中发散问题。
    返回 blocks, num_local_computed_tokens, shared_prefix_boundary, hit_diverged
    """
    coordinator = self.coordinator
    # 仅当为混合模型且存在全注意力组时才进入发散逻辑
    if not (
        self.kv_cache_config.has_mamba_layers
        and isinstance(coordinator, HybridKVCacheCoordinator)
        and coordinator.full_attention_group_id is not None
    ):
        # 非混合模型直接使用普通查找,标记为未发散
        return *self.get_computed_blocks(request), False
​
    if not self.prefix_cache_lookup_enabled(request):
        return self.empty_kv_cache_blocks, 0, 0, False
​
    fa_group_id = coordinator.full_attention_group_id
    computed, per_group_hits = coordinator.find_longest_cache_hit_per_group(
        request.block_hashes, request.num_tokens - 1
    )
    # 情形 1:某组命中比全注意力组更深 -> 全注意力块被驱逐,无法提供一致边界
    if any(hit > per_group_hits[fa_group_id] for hit in per_group_hits):
        return *self.get_computed_blocks(request), False
​
    num_local = per_group_hits[fa_group_id]
    blocks = self.create_kv_cache_blocks(computed)
    # hit_diverged 表示至少有一组比全注意力组浅(发散),
    # 调用者需在无外部 token 时回退
    return blocks, num_local, 0, min(per_group_hits) < num_local
vllm/v1/core/sched/scheduler.py dependency-wiring

修改了调度循环中的前缀查找分支,将发散逻辑委托给 KVCacheManager 并增加 fallback 回退。删除了对 HybridKVCacheCoordinator 的直接依赖。

# 在 schedule 方法中,前缀查找分支
if request.num_computed_tokens == 0:
    did_prefix_cache_lookup = True
    hit_diverged = False
    if self.connector is not None:
        # 使用 connector 感知的混合查找(可能发散)
        (
            new_computed_blocks,
            num_new_local_computed_tokens,
            request.shared_prefix_boundary,
            hit_diverged,
        ) = self.kv_cache_manager.get_computed_blocks_for_connector(request)
    else:
        # 普通查找,返回一致边界
        (
            new_computed_blocks,
            num_new_local_computed_tokens,
            request.shared_prefix_boundary,
        ) = self.kv_cache_manager.get_computed_blocks(request)
​
    # 获取连接器确认的外部匹配 token 数
    ext_tokens = self.kv_cache_manager.get_num_new_matched_tokens(
        request)
    num_external_computed_tokens = ext_tokens
​
    # 当发散且连接器无外部 token 时,回退到各组一致边界
    if hit_diverged and num_external_computed_tokens == 0:
        (
            new_computed_blocks,
            num_new_local_computed_tokens,
            request.shared_prefix_boundary,
        ) = self.kv_cache_manager.get_computed_blocks(request)

评论区精华

将发散处理逻辑从 Scheduler 移至 KVCacheManager 设计

ivanium review 时指出:“I kinda feel this logic belongs to KVCacheManager and KVCacheCoordinator and putting it in the scheduler feels a bit pervasive. Should we add a new KVCacheManager interface for this purpose?”

结论:作者采纳并实现,将逻辑提取到 KVCacheManager.get_computed_blocks_for_connector。 · 已解决

FA-deeper 发散方向未处理,需要在无外部 token 时 fallback 正确性

howarlii 在 Issue 评论中指出:“I think the opposite divergence is still unhandled: FA hit > mamba hit with the connector reporting no external match... Would it make sense to fall back to the reconciled get_computed_blocks boundary when the connector reports 0 new matched tokens?”

结论:作者在第二次提交中增加 hit_diverged 且 num_external_computed_tokens == 0 时的回退逻辑,howarlii 确认 LGTM。 · 已解决

风险与影响

主要风险在于调度器前缀查找路径的修改,可能引入新的逻辑错误或性能退化。但通过新增的两个测试覆盖了主要发散场景,且 reviewer 已批准。get_computed_blocks_for_connector 仅在混合模型且连接器存在时替代原逻辑,非混合模型无影响。返回接口增加了一个 bool 值,调用方已适配。潜在风险是当连接器类型变化或新的混合模型引入时,此逻辑可能需更新。

直接影响使用 KV connector 的混合模型(Mamba + Attention)用户,修复了调度器崩溃的 bug。对同一环境中的非混合模型或未使用 connector 的场景无影响。团队后续维护此部分需要理解 hit_diverged 机制和 fallback 逻辑,但代码已集中在 KVCacheManager 中,降低了认知负担。

核心调度路径变更 混合模型特定逻辑

关联 Issue

#46453 [Bug]: Hybrid Mamba + KV connector: per-group prefix-hit divergence and vllm engine crashed

完整报告

参与讨论