Prhub

#49960 [CPU] Fix torch.compile crash from torch.accelerator.synchronize on CPU-only hosts

原始 PR 作者 ganeshr10 合并时间 2026-08-03 19:48 文件变更 2 提交数 2 评论 7 代码增减 +13 / -2

执行摘要

CPU-only 主机 torch.compile 崩溃修复:empty_cache 改用独立 no-op

PR body 指出:CPU-only 主机上 torch.compile(如 gpt-oss)在 aot_compile 期间崩溃,报 AssertionError: No accelerator available for torch.accelerator.synchronize;synchronize 在 CPU 上无加速器可用,eager 模式会 raise,Dynamo 的 handle_synchronize 也会断言。讨论中 ganeshr10 进一步定位到真正触发点是 vllm/v1/worker/workspace.py:175 的 torch.accelerator.empty_cache()——vLLM 将两个 API 绑到同一 noop 对象,Dynamo 按对象身份分派,导致 empty_cache 被误判为 synchronize。

值得精读的小型修复:改动仅 13 行,但完整展示了一个编译期崩溃 -> 最小复现 -> 根因定位(对象身份别名)-> 极简修复的调试链路。对维护 CPU worker 补丁与接触 torch.compile 图形捕获的同学有参考价值;也提示 Dynamo 对 torch 模块 API 的追踪可能依赖函数对象身份,写 monkey patch 时需避免别名共享。

讨论亮点

bigPYJ1151:Could you provide a example to reproduce the issue? Tried the openai/gpt-oss-20b with the latest main, cant reproduce the issue on both of MRV1 and MRV2.

ganeshr10:最小复现需要无加速器主机 + 冷编译缓存,GPU 可见的机器永远不会触发;并给出 torch==2.13.0+cpu 下 @torch.compile 内调用 torch.accelerator.synchronize 即断言的示例。

ganeshr10:定位结果——被追踪的调用是 torch.accelerator.empty_cache()(workspace.py:175),不是 synchronize;因为 vLLM 把两者绑到同一 noop 对象,Dynamo 的 handle_synchronize 按对象身份分派,所以 empty_cache 被误判。

bigPYJ1151:Let's add such monkey patches in vllm/v1/worker/cpu/shm.py(随后批准合并)。

实现拆解

  1. 根因确认:在 Issue 讨论中通过对照实验证明,只要给 empty_cache 一个独立的 no-op,崩溃即消失,不需要修改 Dynamo。
  2. 主补丁改动(vllm/v1/worker/cpu/shm.py):新增模块级函数 empty_cache_noop,并将 torch.accelerator.empty_cache = noop 改为绑定到 empty_cache_noop,与 torch.accelerator.synchronize = noop 解绑。
  3. 编译路径补丁改动(vllm/v1/worker/cpu_model_runner.py):在 _set_torch_accelerator_to_noop 内部同步新增 empty_cache_noop 并替换 empty_cache 绑定,保证两处 CPU 补丁行为一致(该函数是 vLLM 编译配置上下文的组成部分)。
  4. 验证与配套:未新增专门测试文件;PR 通过 Buildkite CI(#81959),复现依赖 CPU-only torch 构建(如 torch==2.13.0+cpu)与冷编译缓存,作者提供了最小复现脚本证明修复有效。
文件 模块 状态 重要度
vllm/v1/worker/cpu/shm.py CPU 后端 modified 6.07
vllm/v1/worker/cpu_model_runner.py CPU 后端 modified 5.84

关键符号

empty_cache_noop _set_torch_accelerator_to_noop

关键源码片段

vllm/v1/worker/cpu/shm.py core-logic

CPU worker 启动时对 torch 加速器 API 打补丁的核心文件,empty_cache 与 synchronize 的别名在此建立,也是本次修复的主落点

# CPU-only 主机上对 torch 加速器 API 的统一兜底补丁。
# 两个 no-op 必须保持独立对象,否则 Dynamo 的 handle_synchronize
# 会因共享 callable 对象而把追踪到的 empty_cache() 误判为
# synchronize(),导致 torch.compile 在 aot_compile 阶段断言失败。def noop(*args: Any, **kwargs: Any) -> None:
    pass
​
​
# 独立的 empty_cache no-op:与 synchronize 分开,避免 Dynamo 误判。
def empty_cache_noop(*args: Any, **kwargs: Any) -> None:
    pass
​
​
# 统一替换 torch 加速器 API,所有 CPU-only 路径共享这份兜底实现。
torch.accelerator.synchronize = noop
torch.accelerator.empty_cache = empty_cache_noop
vllm/v1/worker/cpu_model_runner.py data-contract

编译设置上下文中同样对 torch.accelerator 打补丁,避免 CPU compile 路径下 Dynamo 误判

def _set_torch_accelerator_to_noop() -> None:
    def noop(*args: Any, **kwargs: Any) -> None:
        pass
​
    # 关键点:empty_cache 必须使用与 synchronize 不同的 no-op 对象。
    # Dynamo 的 handle_synchronize 以被追踪的 callable 对象为 key,
    # 若两者共用同一 noop,torch.compile 下被追踪的 empty_cache()
    # 会被误分派到 synchronize 处理器,在 CPU-only 主机上触发
    # “No accelerator available for torch.accelerator.synchronize” 断言。
    def empty_cache_noop(*args: Any, **kwargs: Any) -> None:
        pass
​
    torch.accelerator.synchronize = noop
    torch.accelerator.empty_cache = empty_cache_noop

评论区精华

复现条件与最小示例 question

bigPYJ1151 在 x86 CPU 上无法复现 gpt-oss 崩溃;ganeshr10 给出无 vLLM 的最小复现:CPU-only torch 构建下 @torch.compile 内调用 torch.accelerator.synchronize 即断言,且需要冷编译缓存

结论:复现依赖 CPU-only torch 构建(如 torch==2.13.0+cpu)与冷编译缓存,GPU 可见的机器不会触发 · 已解决

根因定位:共享 noop 对象触发 Dynamo synchronize 断言 正确性

ganeshr10 定位到 workspace resize 时 aot_compile 调用 torch.accelerator.empty_cache()(workspace.py:175),而 vLLM 把 synchronize 与 empty_cache 都绑到同一 noop 对象(cpu/shm.py:55-56、cpu_model_runner.py:238-239),Dynamo 的 handle_synchronize 以对象为 key,导致 empty_cache 被误判为 synchronize;对照实验确认给 empty_cache 独立 no-op 后崩溃消失

结论:已确认根因并采用“独立 empty_cache no-op”方案修复 · 已解决

修复落点选择 设计

ganeshr10 提出两处可选方案:vLLM 内 2 行 patch 或改 Dynamo handler;bigPYJ1151 决定将 monkey patch 落在 vllm/v1/worker/cpu/shm.py 并批准合并

结论:在 CPU worker 模块的 torch API 补丁处实现,未改动 Dynamo · 已解决

Arm CPU 平台无法复现 question

fadara01 在无 GPU 的 Arm CPU 平台无法复现并请求变更;作者补充了复现环境细节但未在公开讨论中直接回应

结论:未产生代码改动,最终由 bigPYJ1151 批准合并(根因依赖 CPU-only torch 构建,可能受平台 torch 构建方式影响) · closed

风险与影响

依赖 Dynamo 内部实现:修复依据是 Dynamo 以 callable 对象为 key 识别 synchronize 调用;未来 torch 若改变识别方式,补丁可能多余或失效,失效时只是回到崩溃态,不会静默出错。monkey patch 全局性:两处都在 import 阶段改写 torch.accelerator 模块属性,全局生效;但 CPU 上 empty_cache 本就是近似 no-op,语义风险极低。测试覆盖缺口:未新增回归测试,后续重构可能重新引入共享 noop 的问题。影响面:仅 CPU-only 主机、默认 torch.compile 路径的用户受益,GPU / ROCm 路径完全不受影响。

对用户:CPU-only 主机上使用默认 torch.compile(非 enforce_eager)的用户不再遇到 aot_compile 崩溃,gpt-oss 等模型冷编译缓存场景可正常工作;eager 模式下同步调用 raise 的问题也一并规避。对系统:CPU worker 启动阶段多一个独立 no-op 对象,无性能开销;语义上 empty_cache 与 synchronize 在 CPU 上仍是空操作。对团队:为 CPU 后端的 Dynamo 兼容性维护提供了可复用的模式,后续若出现类似 API 共享对象被 Dynamo 特殊处理的问题可参考。

缺少测试覆盖 依赖 Dynamo 内部实现 monkey patch 全局生效

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论