Prhub

#44733 [KV offload] Parallel-agnostic fs-tier cache for single full-attention group

原始 PR 作者 Etelis 合并时间 2026-06-11 20:48 文件变更 4 提交数 9 评论 6 代码增减 +102 / -3

执行摘要

单全注意力组 KV 缓存跨并行尺寸复用

KV offload 缓存目前按并行配置(tp_size、rank 等)命名路径,导致相同内容在不同并行尺寸下无法复用。对于单个全注意力组,KV 块分布与并行度无关,因此可跨并行配置共享缓存,提升缓存命中率并减少I/O。讨论中 orozery 要求将逻辑移到 FileMapper 以便复用。

本PR设计值得精读:将并行无关判断集中于 FileMapper,通过布尔参数让各tier透明受益,是一种清晰的关注点分离。特别是对子类关系的处理(排除 MLA)体现了防御式编程。建议在引入新的注意力类型时同步更新排除条件。

讨论亮点

核心讨论由 reviewer orozery 驱动:

  • 逻辑集中:要求将 parallel_agnostic 判断从 fs tier 移至 FileMapper,以便各 tier 统一受益。作者随后实现。
  • MLA 排除:指出 isinstance(groups[0].kv_cache_spec, FullAttentionSpec) 会匹配 MLAAttentionSpec(子类),导致 MLA 被错误视为并行无关。作者添加 not isinstance(spec, MLAAttentionSpec) 排除。
  • 测试迁移:建议将测试从 test_fs_tier.py 移到 test_file_mapper.py,作者执行。
  • 对象存储层相同修改:要求 obj tier 也做相应改变,作者在后续提交中补上。

实现拆解

  1. 集中判断逻辑至FileMapper:在 vllm/v1/kv_offload/file_mapper.pyfrom_offloading_spec 方法中,从 kv_cache_groups 提取 spec,若唯一 group 且 spec 为 FullAttentionSpec 且非 MLAAttentionSpec,则保留 parallel_agnostic 传入值,否则强制设为 False。同步导入 FullAttentionSpecMLAAttentionSpec
  2. 启用文件系统tier:在 vllm/v1/kv_offload/tiering/fs/manager.py 中调用 FileMapper.from_offloading_spec 时传入 parallel_agnostic=True,将实际裁决交给 FileMapper。
  3. 启用对象存储tier:在 vllm/v1/kv_offload/tiering/obj/manager.py 中做相同修改,使对象存储层也受益于并行无关共享。
  4. 新增单元测试:在 tests/v1/kv_offload/test_file_mapper.py 中添加辅助构造方法(_full_attention_group_sliding_window_group)及四个测试函数,分别验证:单全注意力组启用(tp_size→1, rank→0)、多组禁用、非全注意力(滑动窗口)禁用、MLA 排除。
文件 模块 状态 重要度
tests/v1/kv_offload/test_file_mapper.py 测试 modified 7.3
vllm/v1/kv_offload/file_mapper.py 文件映射 modified 6.11
vllm/v1/kv_offload/tiering/fs/manager.py 文件系统层 modified 4.98
vllm/v1/kv_offload/tiering/obj/manager.py 对象存储层 modified 5.22

关键符号

FileMapper.from_offloading_spec

关键源码片段

tests/v1/kv_offload/test_file_mapper.py test-coverage

新增 parallel_agnostic 完整测试,覆盖启用、禁用、MLA 排除等场景。

def _full_attention_group() -> KVCacheGroupSpec:
    # 构建一个全注意力组的 spec
    return KVCacheGroupSpec(
        layer_names=["layer0"],
        kv_cache_spec=FullAttentionSpec(
            block_size=16, num_kv_heads=4, head_size=128, dtype=torch.float32
        ),
    )def _sliding_window_group() -> KVCacheGroupSpec:
    # 构建一个滑动窗口注意力组的 spec
    return KVCacheGroupSpec(
        layer_names=["layer0"],
        kv_cache_spec=SlidingWindowSpec(
            block_size=16, num_kv_heads=4, head_size=128, dtype=torch.float32,
            sliding_window=128,
        ),
    )# 只有单个全注意力组时,tp_size 被强制为 1,rank 被置为 0
# 从而产生与并行度无关的缓存路径
def test_parallel_agnostic_enabled_for_single_full_attention():
    fm = make_mapper_from_offloading_spec(
        tp_size=2, rank=1,
        kv_cache_groups=[_full_attention_group()],
        parallel_agnostic=True,
    )
    assert fm.fields["tp_size"] == 1
    assert fm.rank == 0# 多个 group 时,即使 parallel_agnostic 传入 True 也应保持原有 tp_size
def test_parallel_agnostic_disabled_for_multiple_groups():
    fm = make_mapper_from_offloading_spec(
        tp_size=2,
        kv_cache_groups=[_full_attention_group(), _full_attention_group()],
        parallel_agnostic=True,
    )
    assert fm.fields["tp_size"] == 2# 非全注意力(滑动窗口)即使只有一个 group 也不启用并行无关
def test_parallel_agnostic_disabled_for_non_full_attention():
    fm = make_mapper_from_offloading_spec(
        tp_size=2,
        kv_cache_groups=[_sliding_window_group()],
        parallel_agnostic=True,
    )
    assert fm.fields["tp_size"] == 2
vllm/v1/kv_offload/file_mapper.py dependency-wiring

核心判断逻辑所在,新增 parallel_agnostic 条件推导。

@classmethod
def from_offloading_spec(
    cls,
    root_dir: str,
    offloading_spec: OffloadingSpec,
    gpu_blocks_per_file: int = 1,
    parallel_agnostic: bool = False,
) -> "FileMapper":
    vllm_config = offloading_spec.vllm_config
    kv_cache_config = offloading_spec.kv_cache_config
​
    parallel_config = vllm_config.parallel_config
    dtype = str(vllm_config.cache_config.cache_dtype).replace("torch.", "")
    kv_cache_groups = [
        {
            "block_size": group.kv_cache_spec.block_size,
            "layer_names": list(group.layer_names),
        }
        for group in kv_cache_config.kv_cache_groups
    ]
​
    # 只有当只有一个 group 且该 group 是 FullAttentionSpec(且不是 MLA)时
    # 才允许启用 parallel_agnostic
    groups = kv_cache_config.kv_cache_groups
    spec = groups[0].kv_cache_spec if len(groups) == 1 else None
    parallel_agnostic = (
        parallel_agnostic
        and isinstance(spec, FullAttentionSpec)
        and not isinstance(spec, MLAAttentionSpec)
    )
​
    return cls(
        root_dir=root_dir,
        model_name=vllm_config.model_config.model,
        hash_block_size=vllm_config.cache_config.block_size,
        gpu_blocks_per_file=gpu_blocks_per_file,
        tp_size=parallel_config.tensor_parallel_size,
        pp_size=parallel_config.pipeline_parallel_size,
        pcp_size=parallel_config.prefill_context_parallel_size,
        dcp_size=parallel_config.decode_context_parallel_size,
        rank=parallel_config.rank,
        dtype=dtype,
        kv_cache_groups=kv_cache_groups,
        parallel_agnostic=parallel_agnostic,
    )

评论区精华

将 parallel_agnostic 逻辑移到 FileMapper 设计

orozery 要求将 fs/manager.py 中的 parallel_agnostic 判断逻辑移到 FileMapper 内部,以便其他 tier 也能受益。作者同意并实现。

结论:逻辑集中到 FileMapper.from_offloading_spec,各 tier 仅传入 True。 · 已解决

排除 MLAAttentionSpec 正确性

orozery 指出 isinstance(spec, FullAttentionSpec) 会匹配 MLAAttentionSpec(子类),但 MLA 的 latent KV 是每 rank 复制的,不能跨并行度共享。作者添加 not isinstance(spec, MLAAttentionSpec) 条件。

结论:显式排除 MLA,保证正确性。 · 已解决

测试迁移到 test_file_mapper.py 测试

orozery 建议将 parallel_agnostic 相关测试从 test_fs_tier.py 移到 test_file_mapper.py,因为测试的是 FileMapper 行为。作者执行。

结论:测试文件迁移完成,test_fs_tier.py 保持不变。 · 已解决

对象存储 tier 同样修改 设计

orozery 要求对象存储 tier 也传入 parallel_agnostic=True,以保持一致性。作者在后续提交中添加。

结论:obj/manager.py 也传入 parallel_agnostic=True。 · 已解决

风险与影响

  • MLA 排除遗漏风险MLAAttentionSpec 继承自 FullAttentionSpec,若未来新增注意力类型未显式排除,可能被错误启用。当前代码明确排除 MLA,相对安全。
  • 缓存一致性风险:跨并行尺寸共享缓存时,若 GPU 数量变化导致显存分配不一致,load 端有 Short read 检查,失败时回退重计算,不会返回错误数据。
  • 无性能风险:仅在文件映射时增加少量条件判断,无运行时开销。
  • 用户:同一模型在不同并行配置间切换时可能命中已有缓存,减少 offload I/O,降低延迟。
  • 系统:向前兼容,现有缓存路径不受影响;新增 parallel_agnostic 参数,扩展新 tier 时需显式传入。
  • 团队:测试覆盖完整,降低回归风险;设计将判断逻辑集中,后续维护清晰。
MLA 排除依赖类继承关系 跨并行尺寸缓存一致性检查 核心路径变更需评审注意力类型扩展

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论