仅在将参数传递给程序时使用 openMP
Posted
技术标签:
【中文标题】仅在将参数传递给程序时使用 openMP【英文标题】:Use openMP only when an argument is passed to the program 【发布时间】:2018-05-20 01:32:22 【问题描述】:只有在将-omp
参数传递给程序的情况下,是否有使用 OpenMP 并行化 for 循环的好方法?
这似乎是不可能的,因为#pragma omp parallel for
是一个预处理器指令,因此甚至在编译时间之前就进行了评估,当然只有在运行时将参数传递给程序才能确定。
目前我正在使用一个非常丑陋的解决方案来实现这一点,这会导致大量的代码重复。
if(ompDefined)
#pragma omp parallel for
for(...)
...
else
for(...)
...
【问题讨论】:
这个***.com/questions/4085595/conditional-pragma-omp 可能很有趣。 【参考方案1】:我认为您正在寻找的可以使用CPU dispatcher technique 来解决。
为了对 OpenMP 代码与非 OpenMP 代码进行基准测试,您可以像这样从相同的源代码创建不同的目标文件
//foo.c
#ifdef _OPENMP
double foo_omp()
#else
double foo()
#endif
double sum = 0;
#pragma omp parallel for reduction(+:sum)
for(int i=0; i<1000000000; i++) sum += i%10;
return sum;
这样编译
gcc -O3 -c foo.c
gcc -O3 -fopenmp -c foo.c -o foo_omp.o
这将创建两个目标文件foo.o
和foo_omp.o
。然后你可以像这样调用这些函数之一
//bar.c
#include <stdio.h>
double foo();
double foo_omp();
double (*fp)();
int main(int argc, char *argv[])
if(argc>1)
fp = foo_omp;
else
fp = foo;
double sum = fp();
printf("sum %e\n", sum);
像这样编译和链接
gcc -O3 -fopenmp bar.c foo.o foo_omp.o
然后我像这样对代码计时
time ./a.out -omp
time ./a.out
在我的 4 核/8 个硬件线程的系统上,第一种情况大约需要 0.4 秒,第二种情况大约需要 1.2 秒。
这是一个只需要一个源文件的解决方案
#include <stdio.h>
typedef double foo_type();
foo_type foo, foo_omp, *fp;
#ifdef _OPENMP
#define FUNCNAME foo_omp
#else
#define FUNCNAME foo
#endif
double FUNCNAME ()
double sum = 0;
#pragma omp parallel for reduction(+:sum)
for(int i=0; i<1000000000; i++) sum += i%10;
return sum;
#ifdef _OPENMP
int main(int argc, char *argv[])
if(argc>1)
fp = foo_omp;
else
fp = foo;
double sum = fp();
printf("sum %e\n", sum);
#endif
这样编译
gcc -O3 -c foo.c
gcc -O3 -fopenmp foo.c foo.o
【讨论】:
【参考方案2】:你可以在运行时通过callingomp_set_num_threads
设置线程数:
#include <omp.h>
int main()
int threads = 1;
#ifdef _OPENMP
omp_set_num_threads(threads);
#endif
#pragma omp parallel for
for(...)
...
这与禁用 OpenMP 不太一样,但它会阻止它并行运行计算。我发现使用命令行开关设置它总是一个好主意(您可以使用 GNU getopt 或 Boost.ProgramOptions 来实现它)。这使您可以轻松地在同一代码上运行单线程和多线程测试。
正如 Vladimir F 在 cmets 中指出的那样,您还可以在执行程序之前通过设置环境变量 OMP_NUM_THREADS
来设置线程数:
gcc -Wall -Werror -pedantic -O3 -fopenmp -o test test.c
OMP_NUM_THREADS=1
./test
unset OMP_NUM_THREADS
最后,您可以在编译时禁用 OpenMP,方法是不为 GCC 提供 -fopenmp
选项。但是,您需要在代码中需要启用 OpenMP 的任何行周围放置预处理器保护(见上文)。如果您想使用 OpenMP 库中包含的某些函数而不实际启用 OpenMP 编译指示,您只需将 -fopenmp
选项替换为 -lgomp
即可链接到 OpenMP 库。
【讨论】:
谢谢,这是一个比我目前拥有的更好的解决方案。尽管如此,我正在做一些基准测试,并且通过使用 OpenMP 的一个线程运行代码并在不使用 OpenMP 的情况下运行代码对速度产生了相当大的影响OpenMP .... 不幸的是,这会给我带来稍微错误的结果。 你不能不向编译器提供 OpenMP 开关吗?自从我使用 OpenMP 已经有一段时间了,但我认为 GCC 仅在您将 -fopenmp 选项传递给它时才启用 OpenMP。 我认为你需要用_OPENMP
预处理宏来保护标题和omp_set_num_threads(threads);
。
当您不想使用 OpenMP 编译指示但仍希望识别 OpenMP 库函数时,更简单的选择是将 -fopenmp
替换为 -lgomp
。此方法是一种等效于 stub OpenMP 编译器开关的方法。
OMP_NUM_THREADS 更简单,你的代码中根本不需要任何东西。【参考方案3】:
如果您不向编译器传递附加标志,一个解决方案是使用预处理器忽略 pragma 语句。
例如,在您的代码中,您可能有:
#ifdef MP_ENABLED
#pragma omp parallel for
#endif
for(...)
...
然后在编译时可以将标志传递给编译器以定义 MP_ENABLED 宏。对于 GCC (and Clang),您将通过 -DMP_ENABLED。
然后你可以用 gcc as 编译
gcc SOME_SOURCE.c -I SOME_INCLUDE.h -lomp -DMP_ENABLED -o SOME_OUTPUT
然后,当您想禁用并行性时,您可以通过删除 -DMP_ENABLED 对编译命令进行小调整。
gcc SOME_SOURCE.c -I SOME_INCLUDE.h -lomp -DMP_ENABLED -o SOME_OUTPUT
这会导致宏未定义,从而导致预处理器忽略编译指示。
您也可以使用 ifndef 来使用类似的解决方案,具体取决于您是否将并行行为视为默认行为。
编辑:正如一些 cmets 所指出的,包含 OMP 库定义了一些宏,例如 _OPENMP,您可以使用它们来代替您自己的用户定义的宏。这看起来是一个更好的解决方案,但工作量的差异相当小。
【讨论】:
我尝试了类似的方法,但没有想到使用编译器/Makefile 来传递标志...这对我来说似乎是一个不错的选择! 当-fopenmp
在符合标准的编译器下定义_OPENMP
时,为什么还要给-DMP_ENABLED
添加用户需求?
关于 __OPENMP 的要点。不确定 OP 是否使用了符合要求的编译器。以上是关于仅在将参数传递给程序时使用 openMP的主要内容,如果未能解决你的问题,请参考以下文章
在 GCC 中将 0 参数传递给可变参数宏失败,但仅在 C++ 中?
将 DateTime 值作为参数传递给 OleDbCommand
使用 ForEach 中的 SwiftUI 3.0 .swipeActions,您如何在将 ForEach 的输入参数传递给该视图时让一个动作转到另一个视图?