10

我需要找出一个可以输入 gcc 的编译器/链接器指令,以便在指定 -fopenmp 时它不会自动链接 libgomp。

原因是我正在尝试针对英特尔的 MKL BLAS 进行构建。MKL 需要添加一个单独的英特尔库来处理多线程(例如,libmkl_intel_thread 或 libmkl_gnu_thread)。然而,用于将 MKL 与 libgomp 链接的库并非在每个操作系统上都可用,包括我的操作系统。这迫使我链接 libmkl_intel_thread,而后者又必须链接到 libiomp5。

虽然我能够构建我的包,但一些二进制文件同时与 libgomp 和 libiomp5 链接。我不肯定这会导致问题,但是已经发生了一些崩溃,链接组合是可疑的,即使它没有导致崩溃,它肯定是一个可怕的低效率。

我正在尝试使用 gcc 4.9.1 来做到这一点。

不幸的是,避免 -fopenmp 不是一种选择。原因是这是为了编译一个由几个子包组成的相当大的包,它们的 Makefile 不是最大的形状,并且以后可能会编译来自其他源(插件)的附加包。强制使用通用编译器/链接器指令并不困难。但是,打开 --enable-openmp 会同时激活 -fopenmp,并定义用于触发与多线程相关的代码。试图将这三个(--enable-openmp、-fopenmp 和链接到--enable-openmp 的代码)分开是不可行的。

我浏览了手册页,但没有看到任何允许选择 openmp 库的 gcc 指令。英特尔的论坛有一个非常古老的讨论,他们建议在 -fopenmp 之后立即指定一个静态库,然后是 --as-needed。这看起来相当摇摇晃晃,并且也有很大的潜力干扰插件包。llvm-openmp 似乎曾经考虑过 -fopenmp=libiomp5 指令,但它似乎已在 3.5 版本中被删除,而且我无论如何都在尝试使用 gcc。

谢谢。

4

3 回答 3

8

GCC 不支持链接到英特尔 OpenMP 运行时库。GCC 的内部代码转换器将 OpenMP 指令转换为ligomp特定的调用,这些调用的 API 与libiomp. 此外,将两个单独的 OpenMP 运行时混合到一个可执行文件中(或者如果动态加载支持 OpenMP 的模块,则混合到一个进程中)会导致灾难。这就是为什么 MKL 的多线程驱动程序有两种风格的原因——一种是 Intel 的,一种是 GNU 的。在某些机器上缺少后者可能是安装的缺陷。

编辑:显然英特尔 OpenMP 运行时提供了一个 GNU 兼容层,这意味着它可以用作libgomp. 至少符号在那里:

$ nm libiomp5.a | sort | grep GOMP_
0000000000000000 T GOMP_barrier@@VERSION
0000000000000000 T GOMP_barrier@GOMP_1.0
0000000000000000 T __kmp_api_GOMP_barrier
0000000000000000 T __kmp_api_GOMP_barrier_10_alias
...

在这种情况下,您需要做的是:

  • 继续-fopenmp编译代码,以便 GCC 识别 OpenMP pragma 并将代码转换为相应的调用libgomp
  • 如果使用 GCC 来链接可执行文件或共享库,请不要-fopenmp在链接阶段将选项传递给它;相反,通过-L/path/to/libiomp5 -liomp5
  • 如果 GNU ld 用于链接可执行文件/模块,请替换-lgomp-liomp5.

如果无法进行上述更改,那么英特尔论坛上的线程是有道理的,因为链接器解析链接时符号引用的方式,尽管它实际上更像是一种 hack。传递-Wl,--as-needed强制 GNU ld 不会DT_NEEDED在命令行上为任何跟随它的库发出标签,除非该库满足未定义的符号引用,假设 GCC 驱动程序将-lgomp在用户提供的选项之后插入某处。这个想法是防止libgomp与可执行文件链接,即使没有对 的未解析引用GOMP_...,通常情况下不应该是这种情况,因为所有引用,即使是来自动态加载的模块的引用,都应该满足libiomp5. 预防libgomp由 RTLD 加载是必不可少的,因为无论是否导入符号,其中都会调用一些构造函数例程,并且这些例程会执行可能会干扰 IOMP 的事情。

链接器技巧不适用于 OS X 等非 ELF 系统。Mach-O 链接编辑器不支持--as-needed,尽管在该操作系统上可能有不同的机制来实现相同的结果。

于 2014-09-23T06:09:45.320 回答
7

我想我现在有一个答案;我和英特尔的人有过几次交流,我想分享一下结果。这是他们的一些建议和我自己想出的混合:

  1. 简短的回答是,你不能。Gcc 希望在链接器阶段强制使用 libgomp。如果 libiomp 也被链接,那么这两个库都将被链接。会叫哪一个?我不知道。

  2. 更长的答案是,在某些发行版上,可以通过创建自定义 libgomp.spec 或更改随 gcc 安装的 libgomp.spec 来更改 gcc 的默认行为(每当设置 -fopenmp 时添加 libgomp)。在我的发行版(自制软件)上,这是不可行的;“libgomp.spec”文件是空的,libgomp 的规范是 gcc 内置的。所有这些都必须被覆盖。每当 gcc 更新时,这都必须重做。

  3. 在某些操作系统上,可以将每个副本和指向 libgomp 的链接替换为指向 libiomp5 的符号链接。然后二进制文件将有多个链接指向同一个库,但有两个不同的名称。那时会发生什么?我不知道。

  4. 我最终做的是从 gcc 转移到 llvm 的 clang-omp 实现。除非另有说明,否则它使用 libiomp5。我对此的担忧是我项目的一部分使用了 fortran,并且没有 llvm fortran 编译器。但事实证明,即使将 -fopenmp 提供给 gfortran,只要 llvm 最终进行链接,它就会清除对 libgomp 的任何引用并用 libiomp5 替换它们。clang-omp 也可以选择使用 -fopenmp=[libiomp5|libgomp] 选择 omp 库,但我无法使其始终如一地工作。无论如何,llvm 3.5 的 clang-omp 实现几乎涵盖了所有的 openmp 规范,到目前为止,似乎没有任何东西在 switch 中丢失。事实上性能有所提高。

  5. 为了记录,我确实尝试使用 gfortran 作为使用 dragonegg 的 llvm 前端。这本书得不偿失。Dragonegg 与 gcc 4.9 不兼容,因此它强制使用 gcc 4.8。很难设置;随着版本的变化,似乎很难维护;llvm 的人不确定 Dragonegg 的前进会有多少支持;并且在所有事件中,性能都不如仅使用 llvm。

  6. 驱使我来到这里的问题是,如何获得一个包含 C 和 fortran 组件的包,它使用 OpenMP,针对 MKL 编译,我的操作系统的 MKL 库与 iomp5 硬链接并且不接受 gomp。答案是唯一可行的选择是从 gcc 迁移到 clang-omp。

  7. 正如 OpenMP 网站上所声称的那样,这确实留下了“iomp5 是否与 gcc 4.9 兼容”的问题。答案很简单,“不”,iomp5 和 gcc 4.9 将无法相互协作 --- 至少无需对工具链进行实质性修改,没有可用的指导或文档,而且尚不清楚是否有人这样做成功地。

于 2014-10-01T20:04:24.520 回答
4

我是英特尔 MKL 团队的技术支持工程师。这篇文章最近引起了我们的注意。英特尔 MKL 的线程层确实需要 libiomp5,即。英特尔 OpenMP 运行时库。要使用 GCC 正确链接 libiomp5,需要在不使用“-fopenmp”的情况下编译代码。然后链接行需要显式包含 libmkl_intel_thread 和 libiomp5。

当前的 MKL 文档没有对此使用模型提供足够的解释。并且 MKL 链接线顾问完全损坏了。对于给您带来的所有不便和困惑,我深表歉意。我们将尽快修复链接线顾问,并将改进用户指南以更好地帮助 OS X 上的 GCC 用户。

于 2014-09-25T16:55:46.590 回答