Prhub

#40917 [Bugfix][Granite4Vision] Fix deepstack buffer causing decode slowdown in compiled mode

原始 PR 作者 artem-spector 合并时间 2026-04-28 15:43 文件变更 1 提交数 1 评论 7 代码增减 +2 / -1

执行摘要

修复 Granite4Vision 编译模式下 deepstack 缓存区导致解码性能下降

PR #40282 引入了 Granite4Vision 模型支持,但在 torch.compile 模式下存在性能缺陷:将完整大小的 deepstack 缓冲区传递给编译模型,导致 Inductor 为全部 max_num_batched_tokens(通常 8192)生成计算核,严重降低解码吞吐。作者 artem-spector 在评论中描述症状为“100% SM utilization during single-sequence decode (should be ~5%), 5x lower generation throughput in compiled vs eager mode”。

此 PR 是一行关键修复,值得所有使用 Granite4Vision 模型的用户关注。它也是说明 torch.compile 下缓冲区形状影响编译器优化的重要案例,对理解 vLLM 编译管线的性能调优有参考价值。

讨论亮点

该 PR 的 Review 评论较少,主要由机器人(Claude、Gemini)自动审查且无实质性反馈;维护者 DarkLight1337 直接批准合并。后续 issue 讨论中,作者请求将修复回溯到 v0.20.1 未果(因该 PR 在 v0.20.0 之后才合并),预计包含在 v0.21 中(约5月13-14日发布)。

实现拆解

  1. 定位问题根因:在 vllm/model_executor/models/granite4_vision.pyforward 方法中,构建 IntermediateTensors 时直接传递了未截取的完整缓冲区 self._ds_buffers[lvl],其大小为 (max_num_batched_tokens, hidden_dim)
  2. 修复缓冲区截取:在构造 IntermediateTensors 之前,计算 inputs_embeds.size(0) 获取当前实际 token 数 n,然后对每个缓冲区执行 self._ds_buffers[lvl][:n] 切片操作,仅将实际有效数据传入模型。
  3. 保持兼容性:切片操作不影响缓冲区零清除逻辑(已使用 buf[:n].zero_()),且对 eager 模式无影响(eager 模式原本就会按实际大小计算)。
文件 模块 状态 重要度
vllm/model_executor/models/granite4_vision.py 模型执行 modified 5.43

关键源码片段

vllm/model_executor/models/granite4_vision.py core-logic

核心修复文件,修改了 forward 方法中 deepstack 缓冲区的切片逻辑,防止完整缓冲区传入编译模型导致内核膨胀。

# 修复前:传入完整缓冲区,导致编译内核处理全部行
if (
    inputs_embeds is not None
    and get_pp_group().is_first_rank
    and self._ds_layer_indices
):
    ds: IntermediateTensors | None = IntermediateTensors(
        {
            f"ds_{llm_layer}": self._ds_buffers[lvl] # 完整缓冲区 (max_num_batched_tokens, hidden)
            for lvl, llm_layer in enumerate(self._ds_layer_indices)
        }
    )
else:
    ds = None# 修复后:按实际 token 数切片,使编译内核按实际批次大小执行
if (
    inputs_embeds is not None
    and get_pp_group().is_first_rank
    and self._ds_layer_indices
):
    n = inputs_embeds.size(0) # 当前实际 token 数
    ds: IntermediateTensors | None = IntermediateTensors(
        {
            f"ds_{llm_layer}": self._ds_buffers[lvl][:n] # 切片后传入实际数据
            for lvl, llm_layer in enumerate(self._ds_layer_indices)
        }
    )
else:
    ds = None

评论区精华

无法回溯到 v0.20.1 导致已发布版本模型损坏 other

作者 artem-spector 发现 PR #40282 被包含在 v0.20.0 中,但该 PR 引入的性能 bug 并未在 v0.20.1 中修复,导致已发布版本中 Granite4.1 Vision 模型性能严重下降。作者请求将修复回溯到下个补丁版本。

结论:维护者 DarkLight1337 解释 v0.20.1 基于 v0.20.0 构建,由于此 PR 未包含在 v0.20.0 中,因此无法回溯到 v0.20.1;预计包含在 v0.21(约 5 月 13-14 日发布)中。 · 已解决

风险与影响

本变更仅修改一行代码(加一行、改一行),且逻辑是常见的张量切片操作,风险极低。可能的风险是如果 inputs_embedsNone 而进入该分支(理论不会,因外层条件已检查 inputs_embeds is not None),会触发 AttributeError。但鉴于条件检查完备,基本无隐患。

直接修复 Granite4Vision 模型在 torch.compile 模式下的严重性能回归,影响所有使用该模型且启用编译的用户。修复后,decode 阶段的 SM 利用率从~100%降至~5%,生成吞吐量提升约5倍。不涉及 API 或配置变更,对非编译用户无影响。

低风险 单行修复

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论