Prhub

#52374 [MRV2] Support attention-free models

原始 PR 作者 njhill 合并时间 2026-08-15 07:54 文件变更 3 提交数 1 评论 2 代码增减 +7 / -2

执行摘要

MRV2 支持 attention-free 模型,补 CI 兼容小改动

PR body 明确说明:"These use MambaHybridModelState And other minor changes for CI compatibility when switching to MRV2 by default. These are split out from https://github.com/vllm-project/vllm/pull/46646." 也就是说,随着 MRV2 即将默认启用,attention-free 模型需要在新的 ModelState 路由下正确获得 mamba 状态管理(此前没有 attention 层的模型会落入 DefaultModelState,无法管理 SSM 状态);另外由于 Qwen3-Next 等 MoE/hybrid 模型在 MRV2 下默认走 V2 runner,依赖 V1 runner 内部 patch 的 Mamba 前缀缓存测试必须显式固定 V1。

值得精读。虽然只有 3 个文件、9 行净变更,但它示范了两件事:一是如何把一个大型迁移 PR 拆成语义单一、可独立合入的小步(关注 #46646 的拆分策略);二是 MambaHybridModelState 复用于 attention-free 模型的 ModelState 路由决策,以及测试中 envs.disable_envs_cache() 处理环境变量缓存的细节。若你正在跟踪 MRV2 默认切换或 mamba 状态管理相关代码,建议阅读 init_model_state 的完整分支顺序与 shutdown 的资源释放序列。

讨论亮点

本 PR 的 review 讨论非常克制,没有出现技术争论:

  • claude[bot] 指出该 PR 来自 fork,自动化 review 默认关闭,需维护者评论 @claude review 才能触发一次性审查。最终未触发额外审查。
  • WoosukKwon 直接 APPROVED 且未留评论,说明维护者认可这一从大 PR 拆出的最小子集。
  • PR body 本身表述了拆分策略:把 MRV2 默认切换所需的 attention-free 支持与 CI 兼容改动从 #46646 中拆出,保持每次变更聚焦、可独立审查。

实现拆解

整个变更可按三步拆解:

  1. 扩展 ModelState 路由条件vllm/v1/worker/gpu/model_states/__init__.pyinit_model_state 中,将原本仅判断 vllm_config.model_config.is_hybrid 的分支扩为 is_hybrid or is_attention_free,让纯 Mamba/SSM 等 attention-free 模型与混合模型一样使用 MambaHybridModelState。该状态类负责 mamba 状态缓存(mamba_cache)的申请、对齐与管理,因此是否包含 attention 层并不影响复用。路由顺序保持不变:模型自定义 get_model_state_cls 优先,其次是 CrossAttention、EncoderOnlyAttention,最后才落到 hybrid/attention-free 与默认分支。

  2. ModelRunner 生命周期与日志微调vllm/v1/worker/gpu/model_runner.pyshutdown 中新增 self.cudagraph_manager = None,在清空 kv_caches 与 attn_groups 之前显式解除对 CUDA Graph 管理器的引用,使 graph 占用的显存能随随后的 gc.collect()empty_cache() 一并回收;同时把模型加载日志从 "Model loading took %s GiB and %.6f seconds" 调整为 "Model loading took %s GiB memory and %.6f seconds",让文案更清晰。

  3. 测试固定 V1 runnertests/v1/e2e/general/test_mamba_prefix_cache.py_run_mamba_prefix_cache_mrv1 新增 monkeypatch.setenv("VLLM_USE_V2_MODEL_RUNNER", "0")envs.disable_envs_cache()。由于该测试会 patch V1 model runner 内部逻辑,而 MRV2 默认开启后 Qwen3-Next 会默认走 V2 runner,必须显式钉住 V1;disable_envs_cache() 用于使 vllm/envs.py 的进程级环境变量缓存失效,否则 setenv 不生效。测试内部仍按原逻辑执行 mamba 前缀缓存的 step 级状态对齐验证,未改变既有断言。

文件 模块 状态 重要度
vllm/v1/worker/gpu/model_states/__init__.py 模型状态 modified 5.5
vllm/v1/worker/gpu/model_runner.py 模型运行器 modified 5.2
tests/v1/e2e/general/test_mamba_prefix_cache.py 前缀缓存 modified 3.83

关键符号

init_model_state ModelRunner.shutdown ModelRunner.load_model _run_mamba_prefix_cache_mrv1

关键源码片段

vllm/v1/worker/gpu/model_states/__init__.py data-contract

核心变更文件:init_model_state 路由条件新增 is_attention_free,直接决定 attention-free 模型在 MRV2 下使用哪个 ModelState 类。

from vllm.config import VllmConfig
from vllm.model_executor.layers.attention import CrossAttention, EncoderOnlyAttention
from vllm.v1.worker.gpu.mm.encoder_cache import EncoderCache
​
​
def init_model_state(
    vllm_config: VllmConfig,
    model: nn.Module,
    encoder_cache: EncoderCache | None,
    device: torch.device,
):
    # 优先让模型自己声明 ModelState,这是最灵活的扩展点。
    if hasattr(model, "get_model_state_cls"):
        cls = model.get_model_state_cls()
        return cls(vllm_config, model, encoder_cache, device)
​
    # 交叉注意力编码器 - 解码器模型(Whisper、CohereASR、NemotronParse 等)
    # 需要额外的编码器缓存管理。
    if any(isinstance(m, CrossAttention) for m in model.modules()):
        from vllm.v1.worker.gpu.model_states.encoder_decoder import (
            EncoderDecoderModelState,
        )
        return EncoderDecoderModelState(vllm_config, model, encoder_cache, device)
​
    # 仅有编码器的模型(BERT/RoBERTa):非因果自注意力,无 KV cache。
    if any(isinstance(m, EncoderOnlyAttention) for m in model.modules()):
        from vllm.v1.worker.gpu.model_states.encoder_only import EncoderOnlyModelState
        return EncoderOnlyModelState(vllm_config, model, encoder_cache, device)
​
    # 关键变更:attention-free(纯 Mamba/SSM 类)模型同样复用
    # MambaHybridModelState。该状态类负责 mamba 状态缓存的管理,
    # 是否包含 attention 层并不影响这一点。
    if vllm_config.model_config.is_hybrid or vllm_config.model_config.is_attention_free:
        from vllm.v1.worker.gpu.model_states.mamba_hybrid import MambaHybridModelState
        return MambaHybridModelState(vllm_config, model, encoder_cache, device)
​
    # 其余常规 Transformer 模型走默认状态管理。
    from vllm.v1.worker.gpu.model_states.default import DefaultModelState
    return DefaultModelState(vllm_config, model, encoder_cache, device)
vllm/v1/worker/gpu/model_runner.py core-logic

ModelRunner 生命周期补强:shutdown 时显式解除 cudagraph_manager 引用,保证 CUDA graph 显存可回收;同时调整模型加载日志文案。

def shutdown(self) -> None:
    """Release GPU tensors (model weights, KV caches, workspace) so that
    memory is reclaimable when running in the same process."""
    torch.accelerator.synchronize()
    # MRV2 下显式解除对 CUDA Graph 管理器的引用,确保其占用的
    # graph 内存能随之后的 gc.collect() 与 empty_cache() 一并回收。
    self.cudagraph_manager = None
    if hasattr(self, "kv_caches"):
        self.kv_caches.clear()
    if hasattr(self, "attn_groups"):
        self.attn_groups.clear()
    if hasattr(self, "kv_cache_config"):
        del self.kv_cache_config
    if hasattr(self, "model_state") and self.model_state.supports_mm_inputs:
        self.model_state.encoder_runner.clear()
    free_before_shutdown(self.vllm_config)
    if hasattr(self, "model_state"):
        del self.model_state
    if getattr(self, "speculator", None) is not None:
        self.speculator = None
    if hasattr(self, "model"):
        del self.model
​
    gc.collect()
    torch.accelerator.empty_cache()
    logger.debug("Cleaned up model weights, KV caches, and workspace")
tests/v1/e2e/general/test_mamba_prefix_cache.py test-coverage

CI 兼容关键改动:该测试会 patch V1 model runner 内部逻辑,而 Qwen3-Next 在 MRV2 默认切换后会走 V2 runner,因此必须显式钉住 V1。

def _run_mamba_prefix_cache_mrv1(
    monkeypatch: pytest.MonkeyPatch, async_scheduling: bool
):
    # 本测试通过 patch 直接侵入 V1 model runner 内部执行逻辑,
    # 而 Qwen3-Next 这类 MoE/hybrid 模型在 MRV2 默认开启后会
    # 默认走 V2 runner,因此必须显式钉住 V1:
    monkeypatch.setenv("VLLM_USE_V2_MODEL_RUNNER", "0")
    # vllm 的环境变量存在进程级缓存,setenv 后必须使缓存失效才会生效。
    envs.disable_envs_cache()
    global async_scheduling_mode
    async_scheduling_mode = async_scheduling
    run_ref_mamba_state_in_subprocess()
    apply_patch(monkeypatch)
    prompt_dataset = datasets.load_dataset("heheda/a_long_article")
    full_prompt = prompt_dataset["train"][0]["text"]
    tests = get_mamba_prefix_cache_step_configs(async_scheduling)
​
    engine = LLM(
        model=MODEL,
        load_format="dummy",
        enable_prefix_caching=True,
        block_size=BLOCK_SIZE,
        mamba_cache_mode="align",
        speculative_config={
            "method": "qwen3_next_mtp",
            "num_speculative_tokens": num_speculative_tokens,
        },
        max_num_batched_tokens=3072,
        hf_overrides={"num_hidden_layers": NUM_HIDDEN_LAYERS},
        async_scheduling=async_scheduling,
        seed=42,
    )

评论区精华

从大 PR #46646 拆出小步子集的审查策略 设计

PR body 说明本 PR 是从 MRV2 大型 PR #46646 中拆出的子集,聚焦 attention-free 模型支持与 CI 兼容小改动,刻意保持变更聚焦、可独立审查。

结论:WoosukKwon 直接 approve,改动被合入,说明拆分粒度获得维护者认可。 · 已解决

fork PR 的自动化审查限制 other

claude[bot] 指出该 PR 来自 fork,自动化 review 默认关闭,可由维护者评论 @claude review 触发一次性审查。

结论:未触发额外自动化审查,由维护者 WoosukKwon 手工 approve 完成。 · closed

风险与影响

风险整体偏低,但有几个点值得关注:

  1. ModelState 路由语义扩展model_states/__init__.py):is_attention_free 模型现在强制走 MambaHybridModelState,该状态类依赖 mamba 状态缓存相关假设。当前纯 SSM/attention-free 模型与 mamba 状态管理语义匹配,但若未来出现既无 attention 又无 SSM 状态的模型,会被误路由。另外检查顺序上 attention-free 分支位于 EncoderOnlyAttention 之后,若某模型同时带编码器注意力与 attention-free 标记,会先命中 EncoderOnlyModelState;当前无此类模型。
  2. shutdown 解引用model_runner.py):self.cudagraph_manager = None 是显式置空而非删除。若 MRV2 后续代码在 shutdown 后仍访问该属性,可能触发 AttributeError。当前生命周期内无可感知影响,但依赖模型运行器 shutdown 后不再使用该属性的隐式约定。
  3. 环境变量全局副作用(测试文件):VLLM_USE_V2_MODEL_RUNNER=0 是进程级全局设置,虽由 pytest monkeypatch 自动恢复,但 disable_envs_cache() 会让同进程其他测试的环境变量读取暂时失效;若该测试与其他用例共享进程,极端情况下可能影响并发读取。
  4. MRV2 默认切换的联动风险:本 PR 只是拼图之一,单看无法验证 MRV2 全局行为;日志文案与 shutdown 改动属配套清理,无独立回归面。

影响范围集中在 MRV2 模型状态分发与相关 e2e 测试:

  • 用户侧:attention-free 模型(纯 Mamba/SSM 类)在启用 MRV2(或 MRV2 默认切换后)可正常运行并获得正确的 mamba 状态管理,不再错误落入默认状态类。
  • 系统侧:模型 shutdown 时 CUDA Graph 管理器引用被显式解除,显存回收更彻底;ModelState 路由条件语义扩展为 hybrid 与 attention-free 两类。
  • 团队侧:为后续 MRV2 默认切换扫清一类模型兼容问题;测试中 "setenv + disable_envs_cache 钉住老 runner" 的模式可被其他依赖 V1 内部实现的测试复用。
  • 影响程度:低到中。改动量小且全部为向前兼容的增量判断,但处于 MRV2 迁移的关键路径上。
MRV2 默认切换前置依赖 模型状态路由语义扩展 测试环境变量全局副作用(已缓解) shutdown 后属性访问隐式约定

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论