使用OpenMP在数组上进行缩减操作是否可行?

22

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应该有类似的东西,如果你有足够数量的线程,使得这种方法有意义,那么累加将通过二叉树进行。

1
也许您可以更详细地解释一下您想要做什么。提供串行代码可能会有所帮助。 - FFox
进一步挖掘,似乎“只有Fortran”是答案。我最终只分配了一个大的本地副本数组放在循环体外,让线程在循环内部累积到自己的副本中,然后在for循环之后仍然在并行区域内对全局数组进行累积,在关键部分内完成。 - Andrew Wagner
1
更深入地挖掘,这里有一篇关于类似主题的研究论文,但它还没有在OpenMP中。 http://www.springerlink.com/content/tq76655852630525/ - Andrew Wagner
如果你想减少开销,你可以使用 atomic 而不是 critical 来保护单个添加操作(甚至可以使用锁数组);你甚至可以使用共享数组的数组而不是私有数组,并尝试自己编写二进制约简。但这会很丑陋。 - Jonathan Dursi
我最终手动分配了线程本地数组的空间。每个线程将 1/8 的累加操作存储到其本地副本中,然后线程在 #pragma omp critical 块内将其本地副本累加到全局副本中。由于核心数(8)远小于 n,因此同步开销可以忽略不计。虽然不太美观,但确实可行。 - Andrew Wagner
使用OpenMP与C++不能被推荐:OpenMP不支持最近的C++标准。对于C++,您可以选择使用std::thread等,或者tbb - Walter
5个回答

9

现在,使用OpenMP 4.5可以对C和C++中的数组进行降维处理。以下是一个示例:

#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/

注意上面的评论,虽然这是方便的语法,但可能会因为为每个线程创建数组副本而产生很多开销。


有点让我不爽的是你的 int main() 没有返回一个 'int' :D - RL-S

3

3
自 OpenMP 4.5 版本以来,现在已经可以使用了;请参见下面的 Chen Jiang 的答案。基本上,您必须指定 _数组区段_(请参见 OpenMP 4.5 规范书第 2.4 节,第 44 页)。您的 #pragma 指令应该像这样:#pragma omp parallel reduction(+:a[:4])但是请注意,您必须小心使用此功能,因为每个线程都会分配其自己的数组区段;如果您在许多线程的大型数组上使用此功能,可能会使内存需求增加。 - Hugo Raguet

2

1

OpenMP 无法对数组或结构变量执行约简操作(请参见限制)。

您可能还想了解privateshared子句。private声明将一个变量对每个线程私有,而shared声明将一个变量在所有线程之间共享。我也发现这个问题的答案对于OpenMP和数组非常有用。


0

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的类型必须与您计划进行缩减的向量匹配。

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