Prhub

#46769 [CPU] Fix macOS/Apple Silicon hang by enabling OpenMP in the build

原始 PR 作者 mgoin 合并时间 2026-06-27 02:32 文件变更 10 提交数 2 评论 2 代码增减 +49 / -15

执行摘要

修复 macOS CPU 因 OpenMP 缺失导致 attention 死锁

macOS CPU 构建从未传递 -fopenmp(#16086 的回归),导致 #pragma omp parallel 区域被编译器省略,但 omp_get_max_threads() 仍报告全部核心数。attention split-KV 路径在区域屏障上等待从未启动的线程团队 → 任何足够长的 prompt 都会死锁。

此 PR 是 macOS 平台的重要 bugfix,值得审查。关键设计是通过 cpu_utils::get_max_threads() 封装线程数获取,实现非 OpenMP 构建的优雅降级。CI 修改确保了持续验证。

讨论亮点

WindChimeRan 批准但指出风险:PR 仅在 macOS 26 (Clang 21) 上验证,可能破坏 macos-15 (Clang 17),且 cpu_utils::get_max_threads() 无法预防加载错误。作者 mgoin 回应 CI 烟雾测试已在 macos-15 上运行通过,风险缓解。最终合入。

实现拆解

  1. 添加 OpenMP 编译标志:在 cmake/cpu_extension.cmake 中为 Apple Clang 添加 -Xpreprocessor -fopenmp,使 #pragma omp parallel 被正确识别。无需额外链接,_C 通过 dynamic_lookup 从 torch 解析 libomp。

  2. 封装线程数获取:在 csrc/cpu/cpu_types.hppcpu_utils 命名空间中新增 get_max_threads(),有 OpenMP 时返回 omp_get_max_threads(),否则返回 1 并给出一次性警告。

  3. 替换所有内核中的 omp_get_max_threads():在 csrc/cpu/cpu_attn_impl.hpp(3 处)、cpu_fused_moe.cppcpu_wna16.cppdnnl_kernels.cppmla_decode.cpp 共 7 处调用点,改为 cpu_utils::get_max_threads()

  4. 改进 CI 烟雾测试:在 .github/workflows/macos-smoke-test.yml 中将 prompt 改为约 260 token 以触发 split-KV 路径;固定矩阵为门控 macos-15 和非阻塞 macos-26。在 .github/actionlint.yaml 中添加预览标签 macos-26。

  5. 更新文档:在 docs/getting_started/installation/cpu.apple.inc.md 中补充 OpenMP 依赖说明。

文件 模块 状态 重要度
csrc/cpu/cpu_types.hpp CPU 内核 modified 6.09
csrc/cpu/cpu_attn_impl.hpp CPU 内核 modified 5.03
csrc/cpu/cpu_fused_moe.cpp CPU 内核 modified 4.54
csrc/cpu/cpu_wna16.cpp CPU 内核 modified 4.54
csrc/cpu/dnnl_kernels.cpp CPU 内核 modified 4.54
csrc/cpu/mla_decode.cpp CPU 内核 modified 4.54
cmake/cpu_extension.cmake 构建系统 modified 2.4
.github/workflows/macos-smoke-test.yml CI 脚本 modified 4.17
.github/actionlint.yaml CI 校验 modified 2.64
docs/getting_started/installation/cpu.apple.inc.md 文档 modified 1.83

关键符号

cpu_utils::get_max_threads

关键源码片段

csrc/cpu/cpu_types.hpp dependency-wiring

新增 cpu_utils::get_max_threads() 封装函数,这是本次修复的核心抽象,确保非 OpenMP 构建安全降级。

#ifndef CPU_TYPES_HPP
#define CPU_TYPES_HPP#if defined(__x86_64__)
  #include "cpu_types_x86.hpp"
#elif defined(__aarch64__)
  #include "cpu_types_arm.hpp"
// ... 其他架构分支省略 ...
#else
  #include "cpu_types_scalar.hpp"
#endif#ifdef _OPENMP
  #include <omp.h>
#endif#include <c10/util/Exception.h>namespace cpu_utils {
// 当没有 OpenMP 时,`#pragma omp parallel` 区域会编译为串行循环,
// 如果内核基于线程数做屏障(barrier),就会导致死锁。
// 因此返回 1,并给出一次性警告。
inline int get_max_threads() {
#ifdef _OPENMP
  return omp_get_max_threads();
#else
  TORCH_WARN_ONCE(
      "vLLM CPU was built without OpenMP; running single-threaded.");
  return 1;
#endif
}
} // namespace cpu_utils#endif

评论区精华

macos-15 兼容性风险 正确性

WindChimeRan 指出 PR 仅在 macOS 26 (Clang 21) 测试,可能破坏 macos-15 (Clang 17),且加载错误无法通过 cpu_utils::get_max_threads() 预防,建议增加 CI 覆盖或限制发布范围。

结论:作者回应 CI 烟雾测试在 macos-15 上已通过,风险可接受,PR 合入。 · 已解决

风险与影响

  • 兼容性风险-Xpreprocessor -fopenmp 在更早 Apple Clang 上可能失效,但 CI 覆盖了 macos-15 (Clang 16) 并通过。
  • 降级风险:非 OpenMP 构建串行运行但不会死锁。
  • 加载风险:libomp 加载失败可能导致 _C 导入错误,但 torch 始终提供 libomp,且加载路径与已有 omp_get_max_threads 一致。
  • 用户影响:macOS CPU 用户可处理长 prompt,attention 多线程加速;不再需要 OMP_NUM_THREADS=1 workaround。
  • 系统影响:无已知负面性能影响。
  • 团队影响:移除临时方案,降低维护成本,CI 更可靠。
OpenMP 兼容性 macOS 版本差异 降级路径

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论