执行摘要
- 一句话:ROCm 修复 GLM-5.2 误入 MRV2,默认关 breakable cudagraph
- 推荐动作:值得精读,尤其是负责 ROCm / model runner / 配置系统的同学。亮点在于:一是用 frozenset 常量 +
lru_cache 平台查询函数集中管理平台默认行为,配合 monkeypatch 与 cache_clear() 形成低成本测试模式;二是 perf 归因方法学(4 组对照分解 MRV2 与 breakable cudagraph 各自的回退贡献)值得借鉴,避免只靠环境变量 workaround 留下约 23% 的性能缺口。同时应记录 mawong-amd 的跟进建议,在 #53712 合并后复测并考虑是否需要反转部分默认值。
功能与动机
PR#52861 将 DSA 模型默认路由到 V2 model runner(MRV2)与 breakable CUDA graphs,但 ROCm 排除集只覆盖了 DeepseekV32ForCausalLM / DeepseekV4ForCausalLM,漏掉了 GlmMoeDsaForCausalLM。PR body 明确指出:"GLM-5.2 (both FP8 and MXFP4) thus became the only DSA model defaulting to MRV2 + breakable cudagraph on ROCm, which regresses decode latency on gfx950 (MI355X) by ~30–37% TPOT at batch 1 in nightly perf-eval." 通过 4 组对照实验分解出 MRV2 贡献约 3.0 ms、breakable cudagraph 贡献约 1.2 ms 的回退,因此需要同时改两处才能完全恢复基线。
实现拆解
- 变更入口:vllm/config/vllm.py 顶部的平台默认配置区,包含
ROCM_DEFAULT_MRV1_ARCHITECTURES 常量与 default_breakable_cudagraph_architectures() 函数。
- MRV1 排除集补漏:把
GlmMoeDsaForCausalLM 加入 ROCM_DEFAULT_MRV1_ARCHITECTURES(原来只有 DeepseekV32 / DeepseekV4 两个架构)。该集合让命中架构在 ROCm 上 use_v2_model_runner 返回 False,从而留在编译过的 MRV1 路径,这是修复约 3.0 ms 回退的关键。
- ROCm breakable 默认全关:
default_breakable_cudagraph_architectures() 的 ROCm 分支从"排除 Deepseek 两个架构"改为直接返回空集 frozenset()。原因是 gfx950 上 breakable cudagraph 对 GLM-5.2 贡献约 1.2 ms 回退,且当前 ROCm 平台没有任何架构从中受益;VLLM_USE_BREAKABLE_CUDAGRAPH=1 的 opt-in 路径保持不变。
- 测试同步:tests/test_config.py 更新
test_rocm_keeps_compiled_deepseek_defaults 的 docstring 与断言(纳入 GlmMoeDsaForCausalLM),并把 test_dsa_breakable_cudagraph_platform_default 参数化用例中 ("GlmMoeDsaForCausalLM", True, True) 翻转为 ("GlmMoeDsaForCausalLM", True, False)。运行方式为 pytest tests/test_config.py -k "rocm or dsa or breakable"。
- 验证与 CI:在 gfx950(MI355X)TP8 上用
vllm bench serve 做对照实验,median TPOT 恢复至 13.46 ms,与 #52861 之前的好基线一致;启动日志确认不再出现 Using V2 Model Runner / Breakable CUDA graph enabled;上游 CI(Buildkite #86417)与 AMD CI(#12493)通过。
关键文件:
vllm/config/vllm.py(模块 配置系统;类别 source;类型 core-logic;符号 ROCM_DEFAULT_MRV1_ARCHITECTURES, default_breakable_cudagraph_architectures): 核心配置逻辑所在:ROCM_DEFAULT_MRV1_ARCHITECTURES 补入 GlmMoeDsaForCausalLM,default_breakable_cudagraph_architectures() 在 ROCm 上改为返回空集。这两处决定了 GLM-5.2 在 ROCm 上回到编译的 MRV1 路径,并让所有架构默认不启用 breakable cudagraph,是修复 30~37% TPOT 回退的关键。
tests/test_config.py(模块 配置测试;类别 test;类型 test-coverage;符号 test_rocm_keeps_compiled_deepseek_defaults, test_dsa_breakable_cudagraph_platform_default): 同步覆盖新默认行为:断言 GLM-5.2 仍在 ROCM_DEFAULT_MRV1_ARCHITECTURES 且不在 ROCm 的 breakable 默认集合中,并把参数化用例里 GLM 在 ROCm 上的期望值翻转为 False,防止后续回归。
关键符号:default_breakable_cudagraph_architectures, test_rocm_keeps_compiled_deepseek_defaults, test_dsa_breakable_cudagraph_platform_default
关键源码片段
vllm/config/vllm.py
核心配置逻辑所在:ROCM_DEFAULT_MRV1_ARCHITECTURES 补入 GlmMoeDsaForCausalLM,default_breakable_cudagraph_architectures() 在 ROCm 上改为返回空集。这两处决定了 GLM-5.2 在 ROCm 上回到编译的 MRV1 路径,并让所有架构默认不启用 breakable cudagraph,是修复 30~37% TPOT 回退的关键。
# vllm/config/vllm.py —— ROCm 平台 DSA 模型默认运行路径配置(PR#53155 之后)
# TODO(rocm): 这些模型在 AMD GPU 上要么不被 MRV2 支持,要么在 MRV2 下更慢。
# 命中该集合的架构在 ROCm 上会留在编译过的 MRV1 路径。
ROCM_DEFAULT_MRV1_ARCHITECTURES = frozenset(
{
"DeepseekV32ForCausalLM",
"DeepseekV4ForCausalLM",
"GlmMoeDsaForCausalLM", # PR#53155 补上漏掉的 GLM-5.2,避免其默认走 MRV2
}
)
# 各平台默认启用 breakable CUDA graph 的架构名单(PR#52861 引入 DSA 路由)。
DEFAULT_BREAKABLE_CUDAGRAPH_ARCHITECTURES = frozenset(
{
"DeepseekV32MTPModel",
"DeepseekV32ForCausalLM",
"DeepseekV4ForCausalLM",
"DeepSeekV4MTPModel",
"Dots3NoteForCausalLM",
"Dots3NoteMTPModel",
"GlmMoeDsaForCausalLM",
"HYV4ForCausalLM",
"HYV4MTPModel",
"InklingForCausalLM",
"InklingForConditionalGeneration",
"KimiK3ForConditionalGeneration",
"KimiK3MTPModel",
"KimiLinearForCausalLM",
"MiniMaxM3SparseForCausalLM",
"MiniMaxM3SparseForConditionalGeneration",
}
)
@lru_cache
def default_breakable_cudagraph_architectures() -> frozenset[str]:
"""返回当前平台默认启用 breakable CUDA graph 的架构集合。"""
from vllm.platforms import current_platform
if current_platform.is_rocm():
# gfx950 实测 breakable cudagraph 贡献约 1.2 ms / 请求 的回退,
# 因此 ROCm 默认不启用任何架构;用户仍可用
# VLLM_USE_BREAKABLE_CUDAGRAPH=1 强制开启。
return frozenset()
return DEFAULT_BREAKABLE_CUDAGRAPH_ARCHITECTURES
tests/test_config.py
同步覆盖新默认行为:断言 GLM-5.2 仍在 ROCM_DEFAULT_MRV1_ARCHITECTURES 且不在 ROCm 的 breakable 默认集合中,并把参数化用例里 GLM 在 ROCm 上的期望值翻转为 False,防止后续回归。
@pytest.mark.parametrize(
("architecture", "is_rocm", "expected"),
[
("DeepseekV32ForCausalLM", False, True),
("DeepseekV32ForCausalLM", True, False),
("GlmMoeDsaForCausalLM", False, True),
# PR#53155 翻转:ROCm 上 GLM-5.2 默认不再启用 breakable cudagraph
("GlmMoeDsaForCausalLM", True, False),
],
)
def test_dsa_breakable_cudagraph_platform_default(
monkeypatch, architecture, is_rocm, expected
):
from vllm.config.vllm import default_breakable_cudagraph_architectures
from vllm.platforms import current_platform
monkeypatch.delenv("VLLM_USE_BREAKABLE_CUDAGRAPH", raising=False)
monkeypatch.setattr(current_platform, "is_rocm", lambda: is_rocm)
# 平台查询函数带 lru_cache,须清缓存才能让 monkeypatch 的平台判断生效
default_breakable_cudagraph_architectures.cache_clear()
config = SimpleNamespace(
model_config=SimpleNamespace(architectures=[architecture]),
compilation_config=CompilationConfig(),
)
config._uses_breakable_cudagraph_by_default = lambda: (
VllmConfig._uses_breakable_cudagraph_by_default(config)
)
try:
assert VllmConfig._maybe_enable_breakable_cudagraph(config) is expected
if expected:
assert config.compilation_config.mode == CompilationMode.NONE
finally:
os.environ.pop("VLLM_USE_BREAKABLE_CUDAGRAPH", None)
default_breakable_cudagraph_architectures.cache_clear()
评论区精华
唯一实质性讨论来自 mawong-amd 的 Issue 评论:"Should revisit the perf numbers here after https://github.com/vllm-project/vllm/pull/53712, and maybe also let DSV4 go back on MRV2"——即 #53712 合并后需要重新评估本 PR 的 perf 结论,并可能让 DeepSeek-V4 重新回到 MRV2 路径。该建议未被深入讨论,作为后续跟进项。claude[bot] 因 fork 来源未自动审查;维护者 AndreasKaratzas 直接 APPROVED(空 body)。此外 mergify[bot] 曾提示合并冲突,作者 rebase main 后解决。
- PR#53712 合并后重新评估 ROCm MRV2 性能与 DSV4 归属 (question): 本 PR 先行合并恢复基线;重新评估留待 #53712 之后跟进。
- fork PR 自动 review 不可用 (other): AndreasKaratzas 直接 APPROVED(空 body),无实质 review 评论。
- 合并冲突与 rebase (other): 已解决,最终 head 为 5037b989070a,CI 重新跑通。
风险与影响
- 风险:
- ROCm 默认行为全局变更:
default_breakable_cudagraph_architectures() 在 ROCm 上直接返回空集,意味着当前在 ROCm 上性能可能尚可的架构(如 KimiK3、Dots3Note、MiniMaxM3 等)也无法默认启用 breakable cudagraph,属于"一刀切"策略,后续需按架构逐个回填。
- 后续结论可能被推翻:mawong-amd 指出 #53712 合并后需重新评估 perf;若 #53712 改善 ROCm 上 MRV2/breakable 性能,本 PR 的部分默认值(尤其 DSV4 归属)可能需要翻转。
- 测试覆盖限制:作者说明 pytest 未在 perf 容器内实际执行,仅靠 CI 覆盖;tests/test_config.py 的断言依赖
lru_cache 清理(cache_clear())与 monkeypatch 模拟平台,若平台判断逻辑演进需同步维护。
- MRV1 编译成本:恢复 MRV1 是编译路径,可能带来更长的启动编译时间,换取运行时 TPOT 收益;对强调快速启动的场景有轻微影响。
- 非 ROCm 无影响:分支条件明确限定
current_platform.is_rocm(),NVIDIA/CPU 等平台不受影响。
- 影响:
- 用户侧:ROCm gfx950 上运行 GLM-5.2(FP8 / MXFP4)的用户,batch 1 decode TPOT 从 17.74 ms 回落至 13.46 ms(约 -24%),恢复至 #52861 之前的基线水平。
- 系统侧:ROCm 平台上所有 DSA 架构默认回到 MRV1 路径且不再默认启用 breakable cudagraph;启动日志行为随之变化。
- 团队侧:AMD 团队需要跟踪 #53712 合并后的 perf 复测,并决定是否让 DeepSeek-V4 / GLM-5.2 重新进入 MRV2;同时为 #41834(NVIDIA SM12x DeepSeek-V4 enablement)等后续工作提供 ROCm 对照基线。
- 非 ROCm:完全不受影响,这是 PR body 明确声明的设计边界。
- 风险标记:ROCm 默认行为全局变更, 配置默认值一刀切, 依赖后续 PR 重新评估, 真机 pytest 未在 PR 内执行
关联脉络
- PR #54646 [Core][MRV2] Freeze gc during V2 CG capture; skip per-descriptor cleanup: 同属 MRV2 / breakable cudagraph 性能优化线(V2 cudagraph 捕获提速约 6 倍),其进展可能影响本 PR 后续在 ROCm 上恢复 breakable cudagraph 默认值的评估结论。
- PR #54373 [Bugfix][Spec Decode] Take the DFlash draft's RoPE layout from its own config: 同属 GLM 模型线(GLM-5.3 的 DFlash 草稿修复),与 GLM-5.2 在 ROCm 上的回归修复互为对照,反映 GLM 系列在 v1 / 投机解码路径上的持续适配。
参与讨论