PAYATHON 2026

如何在 CMake 中为 Intel 编译器正确查找 BLAS 和 OpenMP?

排障赵师傅

可以继续使用 BLAS::BLAS 和 LAPACK::LAPACK,通过 BLA_VENDOR 指定 MKL。使用 Intel 编译器不需要改成 MKL::MKL。

OpenMP::OpenMP_Fortran 缺失需要单独排查。常见原因包括尚未启用 Fortran、切换编译器后沿用了旧缓存、oneAPI 环境加载不完整,或 CMake 对当前 Intel 编译器的检测支持不足。C 的 OpenMP 检测成功,也不能说明 Fortran 环境正常。

使用标准查找模块选择 MKL

CMake 的 FindBLAS 和 FindLAPACK 已支持 MKL。对于使用 32 位整数接口的 64 位程序,可以这样配置:

cmake -S . -B build-intel \
  -DCMAKE_C_COMPILER=icx \
  -DCMAKE_Fortran_COMPILER=ifx \
  -DBLA_VENDOR=Intel10_64lp \
  -DBLA_SIZEOF_INTEGER=4

Intel10_64lp 是 CMake 定义的供应商选项名称,适用于 MKL v10 及后续版本,不要求安装 MKL 10。它选择 LP64、多线程版本;需要顺序版本时,可以改用 Intel10_64lp_seq。参见 FindBLAS 的 MKL 选项说明。

项目中的链接代码可以保持统一。例如,混合 C、Fortran 的可执行目标可以这样写,源文件名按实际项目替换:

cmake_minimum_required(VERSION 3.22)
project(MySolver LANGUAGES C Fortran)

set(BLA_SIZEOF_INTEGER 4 CACHE STRING
    "Size in bytes of BLAS/LAPACK integers")

find_package(BLAS REQUIRED)
find_package(LAPACK REQUIRED)
find_package(OpenMP REQUIRED COMPONENTS C Fortran)

add_executable(solver main.c solver.f90)

target_link_libraries(solver PRIVATE
    LAPACK::LAPACK
    BLAS::BLAS
    OpenMP::OpenMP_C
    OpenMP::OpenMP_Fortran
)

将 BLA_VENDOR 放在配置命令或 CMake Preset 中即可,业务目标无需判断编译器品牌。GNU 构建也可以使用 MKL,Intel 构建也可以选择其他库。

示例要求 CMake 最低为 3.22,因为 BLA_SIZEOF_INTEGER 从该版本开始提供。不过,3.22 并不能保证解决所有后续 Intel 编译器的兼容问题。LAPACK::LAPACK 是标准查找模块提供的导入目标。参见 FindBLAS 和 FindLAPACK。

BLA_SIZEOF_INTEGER=4 只控制所查找库的整数接口,不会修改 Fortran 变量的 kind,也不会改变编译器的默认整数大小。调用代码的整数类型必须与库接口一致。

先加载完整的 oneAPI 环境

即使已经安装 MKL,启动 CMake 的进程也可能找不到它的全部依赖。

在 Linux 上,传统 oneAPI 安装布局通常可以这样初始化:

source /opt/intel/oneapi/setvars.sh

路径以实际安装为准;其他安装布局应使用对应的环境初始化脚本。初始化后,在同一个终端中运行 CMake。

检查是否只加载了 MKL 组件环境。CMake 文档提醒,仅加载 MKL 环境可能无法提供 iomp5,还需要编译器组件环境或其他有效的运行库路径。这可能同时影响多线程 MKL 和 OpenMP 的链接检查。参见 CMake 的 Intel MKL 环境说明。

Windows 上应从已初始化 Intel 编译器环境的命令提示符运行 CMake。如果通过 IDE 配置,也要确认 IDE 进程继承了相同环境。

确认 Fortran 在查找 OpenMP 前已经启用

标准目标的完整名称是:

OpenMP::OpenMP_C
OpenMP::OpenMP_Fortran

OpenMP_Fortran_FOUND 是检测结果变量,不是链接目标。

应在 find_package(OpenMP ...) 前启用 Fortran:

project(MySolver LANGUAGES C Fortran)
find_package(OpenMP REQUIRED COMPONENTS C Fortran)

如果项目最初只启用了 C,之后才调用 enable_language(Fortran),此前的 OpenMP 查找不会自动补做 Fortran 检测。

不指定 COMPONENTS 时,FindOpenMP 会检测当时已启用的语言。显式写出 C Fortran 可以明确所需组件,也能更早发现缺失项。参见 FindOpenMP 文档。

切换编译器时使用新的构建目录

从 GNU 切换到 Intel 时,建议使用全新的构建目录,例如前面的 build-intel。如果只修改 CC、FC 就重新配置旧目录,CMake 可能继续使用缓存中的编译器路径和依赖检测结果。

还要检查 CMake 实际识别到的编译器。可以在 project() 后临时加入:

message(STATUS
    "C compiler: ${CMAKE_C_COMPILER} (${CMAKE_C_COMPILER_ID})")
message(STATUS
    "Fortran compiler: ${CMAKE_Fortran_COMPILER} "
    "(${CMAKE_Fortran_COMPILER_ID})")

Intel Classic 和基于 LLVM 的 Intel 编译器使用不同的 ID,分别为 Intel 和 IntelLLVM。如果项目或自定义查找模块只判断:

if(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel")

这个条件不会匹配 IntelLLVM。参见 CMake 编译器 ID 说明。

根据探测日志定位失败环节

FindOpenMP 会分别测试各语言的编译和链接。Fortran 检测还涉及 omp_lib 模块或 omp_lib.h,因此可能出现 C 检测成功、Fortran 检测失败的情况。

可以使用新的诊断构建目录,保留探测文件并输出查找路径:

cmake -S . -B build-intel-debug \
  -DCMAKE_C_COMPILER=icx \
  -DCMAKE_Fortran_COMPILER=ifx \
  -DBLA_VENDOR=Intel10_64lp \
  --debug-find \
  --debug-trycompile

这两个选项分别用于查看查找过程和保留测试编译目录,具体说明见 CMake 命令行文档。

使用 CMake 3.26 及后续版本时,可以检查:

build-intel-debug/CMakeFiles/CMakeConfigureLog.yaml

较旧版本通常查看 CMakeFiles/CMakeError.log 和 CMakeFiles/CMakeOutput.log。配置日志的用途及位置见 cmake-configure-log。

排查时,重点查看失败测试使用的命令,以及第一条有实际意义的错误:

错误现象优先检查
不认识 OpenMP 编译选项实际编译器、CMake 版本、项目中的全局编译参数
找不到或无法读取 omp_libFortran 安装、模块搜索路径、是否混入其他编译器版本的模块
找不到 iomp5 等库编译器组件环境、库搜索路径、安装组件是否完整
出现 OpenMP 未定义符号实际链接命令是否包含所需选项和运行库
BLAS 库已找到,但测试链接失败MKL 的依赖库、整数接口、架构及链接参数

当前 CMake 的 FindOpenMP 对 Intel 和 IntelLLVM 采用不同的选项处理方式。如果使用较旧的 CMake 搭配较新的 ifx,可以先换用较新的 CMake 稳定版,在全新构建目录中复查。缺少具体版本和日志时,无法确定是哪项兼容问题。相关检测逻辑可见 FindOpenMP 源码。

用最小 Fortran 程序验证编译器环境

也可以绕过 CMake,将以下程序保存为 omp_check.f90:

program omp_check
    use omp_lib
    implicit none

    !$omp parallel
    print *, "thread", omp_get_thread_num()
    !$omp end parallel
end program omp_check

在已初始化环境的 Linux 终端中,用 ifx 编译并运行:

ifx -fiopenmp omp_check.f90 -o omp_check
OMP_NUM_THREADS=2 ./omp_check

-fiopenmp 是 ifx 的 OpenMP 选项,Windows 对应 /Qiopenmp。编译和链接阶段都需要正确启用 OpenMP。参见 Intel Fortran 选项说明。

如果这个程序也无法编译或链接,先根据报错解决模块、运行库或环境问题。如果程序能正常构建并运行,但 CMake 检测失败,就比较两者使用的编译器路径和命令参数,再检查缓存、工具链文件及 CMake 版本。

不要将 OpenMP_Fortran_FOUND 强制设为 TRUE,也不要用 OpenMP::OpenMP_C 代替 Fortran 目标。这些做法会跳过所需检查,Fortran 源文件仍可能缺少正确的编译选项。

备注:内容仅供参考。