Prhub

#52861 [Model][NVIDIA] Route DSA models to the CUDA non-compiled path

原始 PR 作者 WoosukKwon 合并时间 2026-08-19 15:13 文件变更 16 提交数 16 评论 7 代码增减 +260 / -146

执行摘要

DSA 模型改为默认走 CUDA 非编译 MRV2 路径

PR body 明确指出问题根源:The optimized NVIDIA classes are currently unreachable from the model registry. That leaves DeepSeek V3.2 and GLM-5.2 on the generic runner/graph path and prevents their MTP draft model from using the matching implementation.deepseek_v32 的 NVIDIA 优化实现已存在但从未被注册表引用,导致 DSA 模型只能走通用路径,且 MTP draft 模型无法匹配专属实现。该 PR 是 #49790 的重发版本(原 PR 因分支同步后 commits 清零无法恢复),承接 #48597 跟踪的 NVIDIA DSA 路由项,并与 #51915(opt-in ROCm/MXFP4 正确性路径,不改默认路由)明确区分。

值得精读。这是一个典型的默认路径切换 + 平台分派 PR,建议关注三点:

1) vllm/models/deepseek_v32/__init__.py 的 CUDA/非 CUDA 导入分派如何与 registry.py 解耦并保持类名契约;
2) vllm/config/vllm.py 中架构集合 + 环境变量自动写入 + CompilationMode.NONE 的配置自动决策模式,可作为其他模型默认执行路径的样板;
3) capability-gated kernel 与 fallback 的硬件兼容策略。也需注意到其测试主要集中在配置层、真实硬件验证依赖 CI 的代价。

讨论亮点

该 PR 来自 fork,claude[bot] 自动评审被禁用(This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.),因此没有人工 review 评论。核心技术讨论实际沉淀在 commit 演进与维护者 CI 评论中:WoosukKwon 在 CI 结果里给出 NVFP4 TP4 dummy weights 下 concurrency 1 至 1024 的吞吐提升为 +16.42% 至 +8.29%,且每个并发档位候选 mean TPOT 均更低,同时附 GSM8K 94.768% 与 MTP 接受率 79.1% 数据;commit 历史显示早期强制 FP8 KV cache 的设计在 Keep existing DSA FP8 cache requirementSimplify DSA query and cache form selection 两轮中被放松为动态选择,属于已解决的设计权衡。

实现拆解

  1. 模型注册与平台分派vllm/model_executor/models/registry.pyDeepseekV32ForCausalLMGlmMoeDsaForCausalLM 的映射从 deepseek_v2 改为 vllm.models.deepseek_v32,并新增 DeepseekV32MTPModel 注册;vllm/models/deepseek_v32/__init__.pyis_rocm/is_xpu 分派改为 is_cuda() 优先分派,CUDA 上导出 NVIDIA 实现并以 import alias 提供 GlmMoeDsaForCausalLM,其余平台回退通用 deepseek_v2/deepseek_mtp 实现,显式 AMD 模块保留供 opt-in。
  2. 默认执行路径配置vllm/config/vllm.py 把两个 DSA 架构加入 DEFAULT_V2_MODEL_RUNNER_ARCHITECTURES(ROCm 上排除),新增 DEFAULT_BREAKABLE_CUDAGRAPH_ARCHITECTURESdefault_breakable_cudagraph_architectures();新增 _uses_breakable_cudagraph_by_default()_maybe_enable_breakable_cudagraph():未显式设置环境变量且架构命中时自动写入 VLLM_USE_BREAKABLE_CUDAGRAPH=1,并把 CompilationMode 置为 NONE,保留 =0 的 opt-out。
  3. 注意力与 KV cache 兼容vllm/models/deepseek_v32/attention.py 删除 require_fp8_kv_cache 类属性与初始化断言,改为按 is_quantized_kv_cache(self.kv_cache_dtype) 动态推导 _fp8_query_fp8_kv_needs_view,支持未量化 BF16 与标准/压缩 FP8 两种 KV cache 形式。
  4. 推测解码配套vllm/config/speculative.pyhf_config_override()deepseek_v32/glm_moe_dsa 的 MTP draft 架构规范为 DeepseekV32MTPModel;移除 deepseek_v32enforce_eager = True(原 FIXME 注释同步删除),MTP 首次进入 CUDA graph 捕获;vllm/v1/spec_decode/llm_base_proposer.py 小幅适配(具体符号未见 diff,依据变更路径推断)。
  5. 测试与配置配套tests/test_config.py 将原 test_rocm_defaults_deepseek_v4_to_mrv1 扩展为 ROCm 双默认断言,并新增 DSA 模型默认 MRV2 + breakable graph、平台默认、MTP 匹配等参数化测试;tests/compile/fusions_e2e/models.pyconftest.py 移除 DeepSeek V3.2 编译用例(不再走编译路径);kernel 融合测试同步适配。CI 覆盖配置导入路径与 SM100 fused norm/RoPE。
文件 模块 状态 重要度
vllm/config/vllm.py 配置层 modified 8.32
vllm/models/deepseek_v32/__init__.py 模型入口 modified 6.88
vllm/model_executor/models/registry.py 模型注册表 modified 5.88
vllm/config/speculative.py 推测解码 modified 5.97
vllm/models/deepseek_v32/attention.py 注意力层 modified 6.33
tests/test_config.py 配置测试 modified 7.05
vllm/v1/spec_decode/llm_base_proposer.py 提议模块 modified 5.02

关键符号

default_breakable_cudagraph_architectures default_v2_model_runner_architectures _uses_breakable_cudagraph_by_default _maybe_enable_breakable_cudagraph VllmConfig._is_default_v2_model_runner_model SpeculativeConfig.hf_config_override DeepseekV32Attention.__init__

关键源码片段

vllm/config/vllm.py configuration

默认执行路径的核心开关:新增 breakable CUDA graph 架构集与自动启用逻辑,把 DSA 架构纳入 MRV2 默认并定义 ROCm 排除规则。

# vllm/config/vllm.py
# MRV2 默认架构集合:新增 DeepseekV32ForCausalLM 与 GlmMoeDsaForCausalLM,
# 使 DSA 模型在 NVIDIA 上默认走 V2 model runner
DEFAULT_V2_MODEL_RUNNER_ARCHITECTURES = frozenset({
    "DeepseekV2ForCausalLM",
    "DeepseekV32ForCausalLM", # DeepSeek V3.2,本次新增
    "DeepseekV4ForCausalLM",
    "GlmMoeDsaForCausalLM", # GLM-5.2,本次新增
    "GraniteMoeForCausalLM",
    "InklingForCausalLM",
    "InklingForConditionalGeneration",
    "KimiK3ForConditionalGeneration",
    "LongcatFlashNgramForCausalLM",
    "Qwen2MoeForCausalLM",
})# 默认启用可断 CUDA 图(FULL + PIECEWISE)的架构集合
DEFAULT_BREAKABLE_CUDAGRAPH_ARCHITECTURES = frozenset({
    "DeepseekV32MTPModel",
    "DeepseekV32ForCausalLM",
    "DeepseekV4ForCausalLM",
    "DeepSeekV4MTPModel",
    "GlmMoeDsaForCausalLM",
    "InklingForCausalLM",
    "InklingForConditionalGeneration",
    "KimiK3ForConditionalGeneration",
    "KimiK3MTPModel",
    "KimiLinearForCausalLM",
    "MiniMaxM3SparseForCausalLM",
    "MiniMaxM3SparseForConditionalGeneration",
})
​
​
@lru_cache
def default_breakable_cudagraph_architectures() -> frozenset[str]:
    """Architectures defaulting to breakable CUDA graphs on this platform."""
    from vllm.platforms import current_platform
​
    if current_platform.is_rocm():
        # ROCm 保留 DeepSeek V3.2 的编译 MRV1 路径,不自动启用可断图
        return DEFAULT_BREAKABLE_CUDAGRAPH_ARCHITECTURES - {
            "DeepseekV32ForCausalLM",
            "DeepseekV32MTPModel",
        }
    return DEFAULT_BREAKABLE_CUDAGRAPH_ARCHITECTURES
​
​
# 以下两个方法位于 VllmConfig 类内部(类体不完整,仅展示核心逻辑)
class VllmConfig:
    def _uses_breakable_cudagraph_by_default(self) -> bool:
        model_config = self.model_config
        if model_config is None:
            return False
        architectures = set(model_config.architectures)
        return bool(architectures & default_breakable_cudagraph_architectures())
​
    def _maybe_enable_breakable_cudagraph(self) -> bool:
        # 未显式设置环境变量且架构命中默认集合时,自动开启可断 CUDA 图
        if (
            "VLLM_USE_BREAKABLE_CUDAGRAPH" not in os.environ
            and self._uses_breakable_cudagraph_by_default()
        ):
            os.environ["VLLM_USE_BREAKABLE_CUDAGRAPH"] = "1"
            logger.info_once(
                "Auto-enabling VLLM_USE_BREAKABLE_CUDAGRAPH=1. "
                "Set VLLM_USE_BREAKABLE_CUDAGRAPH=0 to opt out."
            )
​
        from vllm.compilation.breakable_cudagraph import (
            is_breakable_cudagraph_enabled,
        )
​
        enabled = is_breakable_cudagraph_enabled()
        if enabled:
            # 可断图路径不再需要 torch.compile,编译模式直接置为 NONE
            self.compilation_config.mode = CompilationMode.NONE
        return enabled
vllm/models/deepseek_v32/__init__.py data-contract

平台分派核心:从 is_rocm/is_xpu 分派改为 is_cuda 优先,CUDA 上导出 NVIDIA 实现并 alias GLM-5.2 类,非 CUDA 平台回退通用实现。

# vllm/models/deepseek_v32/__init__.py
# 平台入口:CUDA 使用 NVIDIA 专属 DSA 实现,其余平台回退通用实现
from vllm.platforms import current_platformif current_platform.is_cuda():
    # GLM-5.2(glm_moe_dsa)复用 CUDA DSA 模块;
    # 能力相关优化 kernel 在模块内部按算力 gate 并自动降级
    from .nvidia.model import DeepseekV32ForCausalLM
    from .nvidia.model import DeepseekV32ForCausalLM as GlmMoeDsaForCausalLM
    from .nvidia.mtp import DeepseekV32MTP
else:
    # ROCm、XPU、CPU 保持通用 deepseek_v2 / deepseek_mtp 实现
    from vllm.model_executor.models.deepseek_mtp import DeepSeekMTP as DeepseekV32MTP
    from vllm.model_executor.models.deepseek_v2 import (
        DeepseekV3ForCausalLM as DeepseekV32ForCausalLM,
    )
    from vllm.model_executor.models.deepseek_v2 import GlmMoeDsaForCausalLM__all__ = [
    "DeepseekV32ForCausalLM",
    "DeepseekV32MTP",
    "GlmMoeDsaForCausalLM",
]
vllm/models/deepseek_v32/attention.py core-logic

移除强制 FP8 KV cache 断言,改为按 KV cache dtype 动态选择查询量化与视图变换,使 BF16 未量化 KV cache 也能走 NVIDIA DSA 路径。

# vllm/models/deepseek_v32/attention.py
# DeepseekV32Attention.__init__ 中 KV cache 与查询量化选择逻辑:
# 移除强制 FP8 KV cache 断言,未量化 BF16 默认启动也能走该路径
fp8_attention = is_quantized_kv_cache(self.kv_cache_dtype)
# FP8 时优先使用支持量化 query 输入的稀疏 MLA 后端
self._fp8_query = fp8_attention and self.impl.supports_quant_query_input
# fp8_ds_mla(FlashMLA 稀疏)布局无需额外 view 变换
self._fp8_kv_needs_view = fp8_attention and self.kv_cache_dtype != "fp8_ds_mla"

评论区精华

Fork PR 自动评审被禁用 other

claude[bot] 提示该 PR 来自 fork,自动评审关闭,维护者可评论 `@claude review` 触发一次性评审。

结论:未产生人工 review 评论,代码质量由 CI 与维护者合入把关。 · closed

NVFP4 TP4 性能对比与 MTP 验证 性能

WoosukKwon 在 CI 结果评论中给出 NVFP4 dummy weights 下 concurrency 1/8/32/256/1024 的吞吐提升 +16.42%/+14.97%/+11.05%/+8.29%/+8.61%,并确认 GSM8K 准确率 94.768%、MTP 接受率 79.1%。

结论:路由切换生效且无明显精度回退,MTP=3 在 GB200 上带来显著加速。 · closed

风险与影响

  • 默认行为变更:所有 NVIDIA GPU 上 DeepSeek V3.2/GLM-5.2 从 generic compiled 路径切到 deepseek_v32 + MRV2 + breakable graph;PR body 声明 pre-SM100 GPU 与 DeepSeek V3.2 实机未做 benchmark,capability gate 与 kernel fallback 只能靠配置测试覆盖。
  • 全局环境变量写入vllm/config/vllm.py_maybe_enable_breakable_cudagraph() 会写入 os.environ["VLLM_USE_BREAKABLE_CUDAGRAPH"],在多模型同进程或测试环境中可能产生跨模型副作用。
  • 编译覆盖下降tests/compile/fusions_e2e 删除 DeepSeek V3.2 编译用例,若未来回退 MRV1 路径将缺少回归护栏。
  • MTP eager 强制移除:DeepSeek V3.2 MTP 首次进入 CUDA graph 捕获,图捕获失败风险主要依赖配置测试与 CI 把关。
  • KV cache 断言放宽attention.py 不再强制 FP8,未量化 BF16 下若 backend 不支持量化 query 会自动走 _fp8_query=False 分支,可能掩盖性能或精度特征变化。
  • 用户侧:NVIDIA 用户默认获得 DSA 专属实现与 MTP 加速,DeepSeek V3.2 默认 KV cache 行为从强制 FP8 变为跟随指定 dtype;ROCm/XPU/CPU 用户无感知(保留通用路径)。
  • 系统侧registry.pyvllm/config/vllm.pyspeculative.py 三处数据契约同步变更;DEFAULT_BREAKABLE_CUDAGRAPH_ARCHITECTURES 同时覆盖 Inkling、KimiK3、MiniMaxM3 等架构,自动启用逻辑对它们的默认行为同样生效,存在连锁影响面。
  • 性能:GLM-5.2 在 4x GB200 上 MTP=3 输出吞吐提升约 2.34 倍(含 TTFT),NVFP4 TP4 下并发 1 至 1024 提升 8% 至 16%。
  • 团队:16 个 commit 呈现参与者早期实现 + 维护者与 Codex 多轮简化的协作模式,测试集中在配置层,真实模型验证依赖大型 GPU CI。
核心路由变更 跨模块配置契约 环境变量全局写入 pre-SM100 硬件无实机验证 编译测试用例移除

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论