忽略不支持OpenMP的机器上的OpenMP。

37

我有一个使用OpenMP的C++程序,它将在多台计算机上运行,这些计算机可能已经安装了OpenMP,也可能没有安装。

如何让我的程序知道一台计算机是否没有安装OpenMP,并忽略那些#include <omp.h>、OpenMP指令(例如#pragma omp parallel ...)和/或库函数(例如tid = omp_get_thread_num();)?


2
请查看安德鲁的答案。据我所知,他是唯一一个实际回答了问题(并且回答正确)的人。还可以参考“-fopenmp”提供了哪些预处理器定义? - jww
5个回答

70

10
不需要保护#pragma omp,因为这种指令不会产生干扰(在未激活openmd时可以安全跳过)。 - YvesgereY
10
尽管如此,如果有人想在使用-Wall选项的情况下编译而不产生警告,那么类似于“警告:未知的#pragma指令被忽略”这样的编译器信息会让人感到苦恼。 - Andrew Dalke
2
关于编译警告,我通常使用-Wno-unknown-pragmas进行编译,出于这个原因。问题是:可能会影响项目中的其他指令,并移除有用的警告。 - johan d
3
这个 if 块很有用,可以隐藏函数调用,例如 omp_set_num_threads() 或 omp_get_thread_num()。#if defined(_OPENMP) tid = omp_get_thread_num(); #else tid = 0; #endif 其中,_OPENMP 宏的定义状态决定了代码是执行 omp_get_thread_num() 还是赋值为0。 - KRoy
@YvesgereY - 关于保护#pragma的使用...GCC符合OpenMP标准,但如果未启用OpenMP,则会发出未知的#pragma警告。这会破坏许多人将其用作安全门的干净编译。另请参见问题66943,GCC警告OpenMP的未知Pragma,尽管它支持它 - jww
显示剩余2条评论

25
编译器应该忽略它们不理解的#pragma指示,这是语法的全部意义。而在非并行系统上,openmp.h中定义的函数具有简单且明确定义的含义——特别地,头文件将检查编译器是否定义了ENABLE_OPENMP,如果未启用,则提供正确的回退。所以,你只需要一个openmp.h的副本来链接即可。这里有一个:http://cms.mcc.uiuc.edu/qmcdev/docs/html/OpenMP_8h-source.html。 但相关代码的部分只是这个:
#if defined(ENABLE_OPENMP)
#include <omp.h>
#else
typedef int omp_int_t;
inline omp_int_t omp_get_thread_num() { return 0;}
inline omp_int_t omp_get_max_threads() { return 1;}
#endif

最坏情况下,你可以将这三行代码放入一个虚拟的openmp.h文件中,并使用它。剩下的部分就可以正常工作了。


12
+1 对于 #pragma 语义。-1 对于 ENABLE_OPENMP,它不符合标准。请使用 _OPENMP - YvesgereY
“...头文件将检查编译器是否定义了ENABLE_OPENMP” - 我不认为这是正确的。你能提供一下ENABLE_OPENMP的参考吗?我在预处理器定义中找不到它。请参见What preprocessor define does -fopenmp provide? - jww
我正在使用clang 3.6.2进行编译,但是clang++ -fopenmp告诉我_OPENMP未定义。 - Arne
1
LLVM的页面似乎表明,OpenMP只在3.8版本中被正确地引入到编译器套件中。早期版本(在FreeBSD上尝试3.6和3.7)似乎接受“-fopenmp”标志,但会忽略它...使用clang-3.8时,定义了“_OPENMP”,并且并行化实际上可以工作。 - Mikhail T.

0

OpenMP是编译器运行时的东西,而不是平台的东西。

也就是说,如果您使用Visual Studio 2005或更高版本编译应用程序,则始终可以使用OpenMP,因为运行时支持它。(如果最终用户没有安装Visual Studio C运行时,则您的应用程序将无法工作)。

所以,您不需要担心,如果您可以使用它,它将始终存在,就像strcmp等函数一样。为确保他们有CRT,您可以安装Visual Studio可再发行版。

编辑:

好的,但GCC 4.1将无法编译您的openMP应用程序,因此问题不在目标机器上,而在目标编译器上。由于所有编译器都具有预定义的宏来给出其版本,请使用#ifdef块包装您的OpenMP调用。例如,GCC使用3个宏来标识编译器版本,__GNUC__,__GNUC_MINOR__和__GNUC_PATCHLEVEL__


我的问题是我想在那些没有多线程的机器上运行程序。低于4.2.x版本的GCC不支持OpenMP。因此,我想让我的Makefile能够识别这一点,并要求g++忽略程序中的OpenMP部分而不是编译失败。有什么想法吗? - Tim
3
编译器版本完全不足够。编译器和运行时都必须支持OpenMP并且还必须启用它。同一个编译器版本在一台计算机上可能支持OpenMP,在另一台计算机上却不支持。 - Jan Hudec

0
如何让我的程序知道某台机器没有安装OpenMP,并忽略那些#include <omp.h>,OpenMP指令(例如#pragma omp parallel ...)和/或库函数(例如tid = omp_get_thread_num();)?
这是一个晚回答,但我们刚刚收到了由于在Microsoft编译器上使用#pragma omp simd而引起的错误报告
根据OpenMP规范第2.2节:

有条件的编译

在支持预处理器的实现中,_OPENMP宏名称被定义为十进制值yyyymm,其中yyyy和mm是实现所支持的OpenMP API版本的年份和月份。

现代的 Microsoft 编译器似乎只支持 OpenMP 从 2000 年至 2005 年之间的某个时间段。我只能说“某个时间段”,因为 OpenMP 2.0 发布于 2000 年,而 OpenMP 2.5 发布于 2005 年。但是 Microsoft 宣传了一个来自 2002 年的版本。

以下是一些 _OPENMP 数字...

  • Visual Studio 2012 - OpenMP 200203
  • Visual Studio 2017 - OpenMP 200203
  • IBM XLC 13.01 - OpenMP 201107
  • Clang 7.0 - OpenMP 201107
  • GCC 4.8 - OpenMP 201107
  • GCC 8.2 - OpenMP 201511

所以如果你想使用,比如说 #pragma omp simd 来保护一个循环,并且 #pragma omp simd 在 OpenMP 4.0 中可用,那么:

#if _OPENMP >= 201307
    #pragma omp simd
    for (size_t i = 0; i < 16; ++i)
        data[i] += x[i];
#else
    for (size_t i = 0; i < 16; ++i)
        data[i] += x[i];
#endif

这个程序将在多台机器上运行,这些机器可能已经安装了OpenMP,也可能没有安装。

需要明确的是,您可能需要在每台机器上构建您的程序。x86_64 ABI不能保证x86、x32或x86_64机器上都可用OpenMP。我也没有看到您可以在一台机器上构建,然后在另一台机器上运行的信息。


由于目标执行平台需要安装编译应用程序所使用的库支持,因此不需要在每台机器上进行构建。在上面的示例中,仅使用pragma omp simd不太可能依赖于库支持。写入应该就足够了。 - tim18
1
#pragma omp simd if(_OPENMP >= 201307) 可以避免多个相同源代码的复制。 - tim18
@tim18 - "... pragma omp simd by itself isn't likely to depend on library support" - 我考虑的使用情况是发行版模型。在i686机器上构建,然后在另一个没有SSE2和友好支持的i686机器上运行。 SSE2和友好支持是x86_64的核心指令集的一部分,但在32位处理器上是可选的。这理论上可以通过运行时调度来实现,但我不知道先决条件/要求在哪里,以便它在实践中起作用。 - jww
@tim18 - 你确认在Visual Studio中使用#pragma omp simd if(_OPENMP >= 201307)和其古老的2002年OpenMP实现是可行的吗?当我尝试在Visual Studio 2015中使用它时,我收到了*test.cpp(8): error C3001: 'simd': expected an OpenMP directive name*的错误提示。 - jww

-2

还有一种方法我很喜欢,借鉴自Bisqwit:

#if defined(_OPENMP)
#include <omp.h>
extern const bool parallelism_enabled = true;
#else
extern const bool parallelism_enabled = false;
#endif

然后,像这样启动您的OpenMP并行for循环:

#pragma omp parallel for if(parallelism_enabled)

注意:有合理的理由不使用非标准的pragma,因此Google和其他公司不支持它。

如果未定义_OPENMP,那么#pragma omp也无法理解,因此在这种情况下if语句将没有任何效果。您还说有合理的理由不使用#pragma,但是您链接的文章是关于#pragma once而不是完全不同的#pragma omp,而且您的解决方案仍然使用了#pragma omp。没有#pragma omp就无法使用OpenMP功能。 - Jake Lishman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接