是否可以使用 openmp 对数组进行缩减?
Posted
技术标签:
【中文标题】是否可以使用 openmp 对数组进行缩减?【英文标题】:Is it possible to do a reduction on an array with openmp? 【发布时间】:2011-04-16 01:34:18 【问题描述】:OpenMP 本身是否支持减少表示数组的变量?
这将像下面这样工作......
float* a = (float*) calloc(4*sizeof(float));
omp_set_num_threads(13);
#pragma omp parallel reduction(+:a)
for(i=0;i<4;i++)
a[i] += 1; // Thread-local copy of a incremented by something interesting
// a now contains [13 13 13 13]
理想情况下,omp parallel for 会有类似的东西,如果你有足够多的线程让它有意义,那么累积将通过二叉树发生。
【问题讨论】:
也许你可以解释一下你想要做什么。提供序列号可能会有所帮助。 再挖掘一下,听起来“仅在 fortran 中”就是答案。我最终只是在循环之外分配了一个大型本地副本数组,让线程在 for 循环中累积到它们自己的副本,然后在 for 循环之后累积到一个全局数组中,仍然在并行区域内,在 a关键部分。 更深入地挖掘,这里有一篇类似的研究论文,但它还没有在 openmp 中。 springerlink.com/content/tq76655852630525 如果你想减少开销,你可以使用 atomic 而不是 critical 来保护单个添加(甚至是锁数组);您甚至可以使用共享数组而不是私有数组,并尝试使用自己的二进制归约。但这会很丑。 我最终手动为数组的线程本地副本分配空间。每个线程将 1/8 的累加到其本地副本中,然后线程将其本地副本累加到 #pragma omp 关键块内的全局副本中。由于核心数 (8) 远小于 n,因此同步开销可以忽略不计。它不漂亮,但它有效。 【参考方案1】:现在可以使用适用于 C 和 C++ 的 OpenMP 4.5 减少数组。这是一个例子:
#include <iostream>
int main()
int myArray[6] = ;
#pragma omp parallel for reduction(+:myArray[:6])
for (int i=0; i<50; ++i)
double a = 2.0; // Or something non-trivial justifying the parallelism...
for (int n = 0; n<6; ++n)
myArray[n] += a;
// Print the array elements to see them summed
for (int n = 0; n<6; ++n)
std::cout << myArray[n] << " " << std::endl;
输出:
100
100
100
100
100
100
我用 GCC 6.2 编译了这个。您可以在此处查看哪些常用编译器版本支持 OpenMP 4.5 功能:https://www.openmp.org/resources/openmp-compilers-tools/
从上面的 cmets 中注意到,虽然这是一种方便的语法,但它可能会因为每个线程创建每个数组部分的副本而导致大量开销。
【讨论】:
你的int main()
没有返回'int'让我有点恼火:D【参考方案2】:
仅适用于 OpenMP 3.0 的 Fortran,并且可能仅适用于某些编译器。
请参阅最后一个示例(示例 3):
http://wikis.sun.com/display/openmp/Fortran+Allocatable+Arrays
【讨论】:
现在可以从 OpenMP 4.5 开始;下面看陈江的回答。基本上,您必须指定 数组部分(请参阅 OpenMP 4.5 规范的第 2.4 节,第 44 页)。你的#pragma 规范看起来像这样:#pragma omp parallel reduction(+:a[:4])
但是要小心,你必须意识到每个线程都会分配它自己版本的数组部分;如果您在具有许多线程的大型数组上执行此操作,则可能会使您的内存需求爆炸。【参考方案3】:
现在最新的 openMP 4.5 规范支持减少 C/C++ 数组。 http://openmp.org/wp/2015/11/openmp-45-specs-released/
而且最新的 GCC 6.1 也支持了这个特性。 http://openmp.org/wp/2016/05/gcc-61-released-supports-openmp-45/
但我还没有尝试过。希望其他人可以测试此功能。
【讨论】:
【参考方案4】:OpenMP 无法对数组或结构类型变量执行归约(请参阅restrictions)。
您可能还想阅读private
和shared
子句。 private
声明一个变量为每个线程私有,而shared
声明一个变量在所有线程之间共享。我还发现这个question 的答案对于 OpenMP 和数组非常有用。
【讨论】:
【参考方案5】:OpenMP 可以从 OpenMP 4.5 开始执行此操作,并且 GCC 6.3(可能更低)支持它。示例程序如下所示:
#include <vector>
#include <iostream>
int main()
std::vector<int> vec;
#pragma omp declare reduction (merge : std::vector<int> : omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end()))
#pragma omp parallel for default(none) schedule(static) reduction(merge: vec)
for(int i=0;i<100;i++)
vec.push_back(i);
for(const auto x: vec)
std::cout<<x<<"\n";
return 0;
请注意,omp_out
和 omp_in
是特殊变量,declare reduction
的类型必须与您计划减少的向量相匹配。
【讨论】:
以上是关于是否可以使用 openmp 对数组进行缩减?的主要内容,如果未能解决你的问题,请参考以下文章