Prhub

#42202 [Model Runner V2] Fix `seq_lens_cpu_upper_bound`

原始 PR 作者 njhill 合并时间 2026-05-12 01:37 文件变更 2 提交数 2 评论 1 代码增减 +18 / -35

执行摘要

修复 MTP 模式下 CPU mirror 的 computed tokens 发散问题

跟随 PR #40654 之后,发现 num_computed_tokens_np 在每个 step 仅通过 num_scheduled_tokens 递增,在 MTP 场景下会无限发散。正确的做法是每个 step 从 scheduler 拿调整后的值直接刷新,而非在 model runner 侧递增。

建议精读 update_requestsis_prefilling 的改动逻辑,理解 computed tokens 状态同步方式;可关注后续是否添加性能优化。

讨论亮点

Review 中 gemini-code-assist[bot] 指出新的 update_requests 循环中每次请求都要做字典查找和标量赋值,在大量缓存请求时可能成为性能瓶颈,建议未来矢量优化。WoosukKwon 最终 approve。

实现拆解

  1. 修改 update_requests 方法(model_runner.py):将原来仅追加新 block 的逻辑扩展为同时从 scheduler 的 reqs.num_computed_tokens 直接覆写 self.req_states.num_computed_tokens_np[req_index],消除了自增导致的发散。
  2. 移除 postprocess/postprocess_pool 中的增量逻辑:删除原先在 postprocesspostprocess_pool 中对 num_computed_prefill_tokensnum_computed_tokens_np+= 操作,改为在 update_requests 中统一通过 np.minimum 同步计算 num_computed_prefill_tokens
  3. 重命名 any_prefillsis_prefilling(states.py):将 ModelState.any_prefills 方法改为 is_prefilling,返回逐请求的 bool 数组而非标量 np.any,便于调用方直接使用。同时利用 Python 链式比较简化 add_request 中的条件判断。
  4. 调用侧适配(model_runner.py):在 prepare_inputs 中将内联的 is_prefilling 计算替换为 self.req_states.is_prefilling(idx_mapping_np) 调用,复用新方法。
文件 模块 状态 重要度
vllm/v1/worker/gpu/model_runner.py 模型运行器 modified 7.22
vllm/v1/worker/gpu/states.py 状态管理 modified 6.26

关键符号

update_requests prepare_inputs is_prefilling add_request

关键源码片段

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

核心变更文件:修改了 `update_requests` 和 `prepare_inputs` 方法,将 computed tokens 的刷新从 postprocess 移到 update_requests,并删除了 postprocess 中的增量逻辑。

# vllm/v1/worker/gpu/model_runner.pydef update_requests(self, scheduler_output: SchedulerOutput) -> None:
    # 更新现有请求的 block 和 num_computed_tokens
    reqs = scheduler_output.scheduled_cached_reqs
    num_computed_tokens_np = self.req_states.num_computed_tokens_np
    for req_id, num_computed_tokens, req_new_block_ids in zip(
        reqs.req_ids, reqs.num_computed_tokens, reqs.new_block_ids
    ):
        req_index = self.req_states.req_id_to_index[req_id]
        # 关键修复:直接用 scheduler 的值刷新,避免自增发散
        num_computed_tokens_np[req_index] = num_computed_tokens
        if req_new_block_ids is not None:
            self.block_tables.append_block_ids(
                req_index, req_new_block_ids, overwrite=False
            )
​
    # 同步更新 computed prefill tokens(取 min 限制不超过 prefill_len)
    np.minimum(
        self.req_states.num_computed_tokens_np,
        self.req_states.prefill_len.np,
        out=self.req_states.num_computed_prefill_tokens,
    )
vllm/v1/worker/gpu/states.py refactor

重命名 `any_prefills` 为 `is_prefilling`,并改变返回值为逐请求数组而非聚合布尔值,简化调用方逻辑。

# vllm/v1/worker/gpu/states.pydef is_prefilling(self, idx_mapping_np: np.ndarray) -> np.ndarray:
    # 返回逐请求 bool 数组,比原 any_prefills 的标量更灵活
    return (
        self.num_computed_prefill_tokens[idx_mapping_np]
        < self.prefill_len.np[idx_mapping_np]
    )def add_request(self, req_id, prompt_len, all_token_ids, num_computed_tokens):
    # ... 其他逻辑 ...
    # 使用 Python 链式比较简化条件
    if 0 < num_computed_tokens <= prefill_len:
        # 设置 last_sampled_tokens
        self.last_sampled_tokens[req_idx : req_idx + 1] = all_token_ids[
            num_computed_tokens - 1
        ]
    # 移除重复的 num_computed_tokens_np 赋值(原先有两行)

评论区精华

update_requests 循环性能 性能

gemini-code-assist[bot] 指出新循环中每次请求的字典查找和标量赋值在大量缓存请求时可能成为性能瓶颈。

结论:当前实现正确修复了发散问题,未来可考虑矢量优化。 · 已解决

风险与影响

核心路径(每次 step 都会执行的 update_requestsprepare_inputs)变更,存在回归风险:如果 scheduler 传回的 num_computed_tokens 与 model runner 预期不一致,可能导致 computed tokens 错误。当前未包含测试文件,缺少覆盖此逻辑的单元测试或集成测试。

影响范围集中在 v1 model runner 的请求状态管理,尤其对 MTP 和 PD disagg 场景有修复作用。用户无感知,但稳定性和正确性提升。

核心路径变更 缺少测试覆盖

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论