Prhub

#43988 [Bugfix] Use storage_block_size in KV cache reshape for compressed specs (DeepSeek V4)

原始 PR 作者 zixi-qi 合并时间 2026-05-30 02:14 文件变更 1 提交数 1 评论 3 代码增减 +6 / -1

执行摘要

修复 DeepSeek V4 KV cache reshape 越界崩溃

DeepSeek V4 的 fp8 Lightning-Indexer KV cache 在初始化时因 _reshape_kv_cache 计算错误导致所有 worker 崩溃,错误信息为 RuntimeError: setStorage: sizes ... requiring a storage size of 6816669136 are out of bounds for storage of size 53255232。该 bug 由 #38831 引入,其将 kv_cache_shape[0]num_blocks 改为 kernel_num_blocks 时未考虑压缩规格的 storage_block_sizeblock_size 差异。

值得所有使用 DeepSeek V4 或类似压缩 KV cache 模型的用户及时合并。代码改动很小,但根本原因分析深入,体现了对 KV cache 布局的理解,适合精读以学习类似问题的排查方法。

讨论亮点

Review 中 njhill 指出注释过于冗长("The comment is a bit verbose, I think it could be conveyed in fewer lines?"),作者 zixi-qi 随后将注释缩短至 3 行,获得批准。此外,MengqingCao 在 Issue 评论中表达了遗漏 DeepSeek V4 场景的歉意,并提议在 #43607 中添加测试用例。

实现拆解

  1. 定位根本原因:在 vllm/v1/worker/gpu/attn_utils.py_reshape_kv_cache 函数中,第 202 行原使用 kv_cache_spec.block_size // kernel_block_size 计算 num_blocks_per_kv_block。对于压缩规格(DeepSeek V4),block_sizestorage_block_sizecompress_ratio 倍,导致 num_blocks_per_kv_block 多出 compress_ratio 倍,进而使 kernel_num_blocks 超量,stride 计算时访问越界。
  2. 修复方案:将 block_size 替换为 storage_block_size,即 kv_cache_spec.storage_block_size // kernel_block_size。对于压缩规格,storage_block_size == kernel_block_size,因此 num_blocks_per_kv_block = 1kv_cache_shape[0] == num_blocks,符合 page_size_padded 分支的假设。对于非压缩规格,storage_block_size 返回 block_size,行为不变。
  3. 测试验证:算术验证显示修复后所需存储大小从 6,816,669,136 B 降低至 53,254,672 B,完全适配实际分配 53,255,232 B。端到端测试(DeepSeek V4 Flash, 4×GB200, VLLM_USE_V2_MODEL_RUNNER=1)显示服务器正常启动并完成推理。GSM8K 5-shot 评测得分为 0.9567,确认正确性。
  4. 代码变更简洁:仅修改一行关键计算,并添加简化注释说明原因。
文件 模块 状态 重要度
vllm/v1/worker/gpu/attn_utils.py 注意力层 modified 6.1

关键符号

_reshape_kv_cache

关键源码片段

vllm/v1/worker/gpu/attn_utils.py core-logic

定位并修复了 KV cache reshape 中压缩规格的 block 计数错误,单行核心逻辑变更。

# vllm/v1/worker/gpu/attn_utils.py 中 _reshape_kv_cache 函数的关键逻辑
if isinstance(kv_cache_spec, AttentionSpec):
    has_attn = True
    # 使用 storage_block_size 而非 block_size:对于压缩规格(如 DeepSeek V4),
    # block_size 是 storage_block_size 的 compress_ratio 倍,用 block_size
    # 会导致 num_blocks_per_kv_block 多出 compress_ratio 倍,触发 buffer 越界。
    num_blocks_per_kv_block = (
        kv_cache_spec.storage_block_size // kernel_block_size
    )
    kernel_num_blocks = num_blocks * num_blocks_per_kv_block
    kv_cache_shape = group.backend.get_kv_cache_shape(
        kernel_num_blocks,
        kernel_block_size,
        kv_cache_spec.num_kv_heads,
        kv_cache_spec.head_size,
        cache_dtype_str=cache_dtype,
    )
    # 后续 strided view 处理中,page_size_padded 分支假设 kv_cache_shape[0] == num_blocks,
    # 修复后该假设对压缩规格也成立。

评论区精华

注释长度讨论 style

njhill 认为注释过于冗长,建议精简。

结论:作者将注释缩短至 3 行,清晰表述核心原因。 · 已解决

压缩规格测试缺失 测试

MengqingCao 在 Issue 评论中表示遗漏了 DeepSeek V4 场景,提议添加测试用例。

结论:尚未在本次 PR 中实现,将在后续 PR #43607 中补充。 · unresolved

风险与影响

风险极低,因为变更幅度很小(仅修改一行计算逻辑),且对非压缩规格无行为变更。但当前没有直接针对压缩规格的回归测试,未来类似重构可能再次引入相同 bug。建议添加针对 DeepSeek V4 压缩 KV cache 的单元测试。

影响范围:所有使用 V2 model runner 且具有压缩 KV cache 规格的模型,目前主要是 DeepSeek V4。修复使得 DeepSeek V4 在 VLLM_USE_V2_MODEL_RUNNER=1 下可正常启动和推理。对非压缩模型(如大多数主流模型)无任何影响。影响程度高,因为它解决了启动崩溃的阻塞性问题。

缺少测试覆盖

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论