Prhub

#43142 [kv_offload]: Add DSv4 support

原始 PR 作者 orozery 合并时间 2026-05-24 16:10 文件变更 4 提交数 9 评论 7 代码增减 +214 / -14

执行摘要

为 offloading 连接器添加 DeepSeek V4 支持

DeepSeek V4 引入了混合 KV 缓存架构,原有的 offloading 连接器无法正确运行。本 PR 适配 resolve_kv_cache_block_sizes 并过滤空 shared_by 张量来解决不兼容问题,同时引入前缀缓存优化来提升混合架构下的 offloading 性能。

本 PR 值得精读,特别是 _alignment_block_count 函数的实现,它展示了如何利用模型结构对齐特性降低 offloading 开销。同时,worker 中的空张量过滤和未填充页面大小修复也是重要的兼容性改进。

讨论亮点
  • 复杂度权衡:NickLucche 评论调度器中对齐逻辑增加了复杂度("man this looks like more complexity :(")。orozery 同意但解释这是针对 DSv4 的重要优化,并提到未来 connector v2 会自动消除这些特殊处理。最终 NickLucche 批准了该 PR。
  • 导入必要性:NickLucche 询问 base.py 中导入 resolve_kv_cache_block_sizes 是否必要。orozery 回复已移到模块顶部导入,确认是必须的。

实现拆解

  1. 统一 hash_block_size 解析:在 OffloadingSpec.__init__ 中,将原先直接使用 vllm_config.cache_config.block_size 计算改为调用 resolve_kv_cache_block_sizes 函数,确保与调度器使用的 block_hashes 一致。涉及文件 vllm/v1/kv_offload/base.py
  2. 过滤空 shared_by 张量:在 worker.pyregister_kv_caches 中,遍历 kv_cache_tensors 时,过滤掉 shared_by 中没有实际模型层的条目(由 _get_kv_cache_config_deepseek_v4 产生),避免后续处理出错。
  3. 使用未填充页面大小:在 worker.pyregister_kv_caches 中,将 unpadded_page_size_bytes 的计算改为使用 layer_kv_cache_spec.real_page_size_bytes(Flash Attention 情况除以 2),确保传输使用实际数据大小。
  4. SWA 对齐跳过优化:在 scheduler.pyGroupOffloadConfig 中新增 alignment_block_count 字段,并在 SchedulerOffloadConfig.from_spec 中根据 full attention 组和 SWA 组的块大小关系计算该值。当 load 命中总是以全 attention 块对齐时,可以跳过不与全 attention 块末尾对齐的 SWA 块的存储,减少 offloading 操作。
  5. 新增单元测试:在 test_scheduler.py 中新增 test_swa_alignment_skip,模拟 DSv4 混合架构,验证 alignment_block_count 计算正确以及 SWA 块存储跳过行为。
文件 模块 状态 重要度
vllm/distributed/kv_transfer/kv_connector/v1/offloading/scheduler.py 调度器 modified 7.77
vllm/distributed/kv_transfer/kv_connector/v1/offloading/worker.py Worker modified 6.12
vllm/v1/kv_offload/base.py 基础抽象 modified 5.97
tests/v1/kv_connector/unit/offloading_connector/test_scheduler.py 测试 modified 6.8

关键符号

_alignment_block_count SchedulerOffloadConfig.from_spec OffloadingSpec.__init__ register_kv_caches test_swa_alignment_skip

关键源码片段

vllm/distributed/kv_transfer/kv_connector/v1/offloading/scheduler.py core-logic

核心变更:引入 alignment_block_count 字段和对齐跳过优化,是支持 DSv4 混合架构的关键

# 在 GroupOffloadConfig 中新增 alignment_block_count 字段
class GroupOffloadConfig(NamedTuple):
    group_idx: int
    gpu_block_size: int
    offloaded_block_size: int
    hash_block_size_factor: int
    # None below means full attention
    sliding_window_size_in_blocks: int | None
    # 每个 full attention 对齐段内该组的 offloaded 块数
    # 用于跳过那些 load 无法命中的 SWA 块(例如 DeepSeek V4 中
    # SWA 组块大小远小于 MLA 全注意组)
    alignment_block_count: int | None = Noneclass SchedulerOffloadConfig(NamedTuple):
    ...
    @classmethod
    def from_spec(cls, spec: OffloadingSpec) -> "SchedulerOffloadConfig":
        # 收集所有 full attention 组的 offloaded_block_size
        full_attn_offloaded_block_sizes: set[int] = set()
        for idx, gpu_block_size in enumerate(spec.gpu_block_size):
            kv_spec = spec.kv_cache_config.kv_cache_groups[idx].kv_cache_spec
            sw = get_sliding_window_size_in_blocks(
                kv_spec, gpu_block_size * spec.block_size_factor
            )
            if sw is None: # full attention group
                full_attn_offloaded_block_sizes.add(
                    gpu_block_size * spec.block_size_factor
                )
​
        # 仅当存在唯一 alignment 大小时应用优化
        alignment_tokens: int | None = None
        if len(full_attn_offloaded_block_sizes) == 1:
            alignment_tokens = full_attn_offloaded_block_sizes.pop()
​
        def _alignment_block_count(
            offloaded_block_size: int,
            sliding_window_size_in_blocks: int | None,
        ) -> int | None:
            if alignment_tokens is None or sliding_window_size_in_blocks is None:
                return None
            if alignment_tokens <= offloaded_block_size:
                return None
            per_segment = alignment_tokens // offloaded_block_size
            if sliding_window_size_in_blocks >= per_segment:
                return None
            return per_segment
​
        return cls(
            num_workers=spec.vllm_config.parallel_config.world_size,
            kv_group_configs=tuple(
                GroupOffloadConfig(
                    group_idx=idx,
                    gpu_block_size=gpu_block_size,
                    offloaded_block_size=gpu_block_size * spec.block_size_factor,
                    hash_block_size_factor=(
                        (gpu_block_size * spec.block_size_factor)
                        // spec.hash_block_size
                    ),
                    sliding_window_size_in_blocks=(
                        sw := get_sliding_window_size_in_blocks(
                            spec.kv_cache_config.kv_cache_groups[idx].kv_cache_spec,
                            gpu_block_size * spec.block_size_factor,
                        )
                    ),
                    alignment_block_count=_alignment_block_count(
                        gpu_block_size * spec.block_size_factor, sw
                    ),
                )
                for idx, gpu_block_size in enumerate(spec.gpu_block_size)
            ),
            block_size_factor=spec.block_size_factor,
        )

评论区精华

对齐跳过优化增加复杂度 设计

NickLucche 评论调度器中对齐逻辑增加了复杂度("man this looks like more complexity :(")。orozery 承认并解释这是针对 DSv4 的重要优化,未来 connector v2 会消除特殊处理。

结论:保持该优化,等待 connector v2 重构。 · 已解决

base.py 导入 resolve_kv_cache_block_sizes 的必要性 question

NickLucche 询问 base.py 中是否必须导入 resolve_kv_cache_block_sizes。

结论:orozery 回复已移到模块顶部导入,确认是必要的。 · 已解决

风险与影响

  1. 复杂度风险:新增的对齐跳过逻辑增加了 SchedulerOffloadConfig 的计算分支,可能在其他混合模型(如 Mamba+Attention)上产生意外行为,但条件判断仅在满足对齐条件时启用,风险可控。
  2. 回归风险:修改了 hash_block_size 的计算方式,可能影响所有使用 offloading 的模型,但新方法使用 resolve_kv_cache_block_sizes 与调度器保持一致,实际更健壮。
  3. 兼容性风险:过滤空 shared_by 张量改变了 worker 的注册流程,如果其他场景产生类似空张量但需要处理的情况,可能被错误跳过。当前只有 DSv4 产生此类空张量,跳过是合理的。
  1. 用户影响:使用 DeepSeek V4 模型并进行 KV offloading 的用户现在可以正常使用,并获得 SWA 存储量的显著减少(约 78%)。
  2. 系统影响:新增了 vllm/v1/kv_offload/base.pyvllm/v1/core/kv_cache_utils 的依赖,模块间耦合增加。
  3. 团队影响:维护复杂度增加,但团队计划通过未来的 connector v2 重构来消除这些特殊处理。
复杂度增加 模块依赖变更 回归风险:hash_block_size 解析

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论