是否可以使用 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)。

您可能还想阅读privateshared 子句。 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_outomp_in 是特殊变量,declare reduction 的类型必须与您计划减少的向量相匹配。

【讨论】:

以上是关于是否可以使用 openmp 对数组进行缩减?的主要内容,如果未能解决你的问题,请参考以下文章

并行 OpenMP 缩减与函数定义?

在 std::vector 上的 Openmp 和缩减?

不了解使用最大运算 OpenMP 程序并行减少的输出?

结合 OpenMP 和 OpenCL

使用 OpenMP 并行化 C 中的基数排序

OpenMP:锁定对单个数组元素的访问?