关于 Zboson 的回答,我有两点意见:
1. 方法 1 肯定是正确的,但归约循环实际上是串行运行的,因为 #pragma omp critical 这当然是必要的,因为部分矩阵对于每个线程都是本地的,并且相应的归约必须由该矩阵的线程完成。
2. 方法2:初始化循环可以移到单个部分之外,因此可以并行化。
以下程序实现数组缩减使用 openMP v4.0 用户定义缩减工具:
/* Compile with:
gcc -Wall -fopenmp -o ar ar.c
Run with:
OMP_DISPLAY_ENV=TRUE OMP_NUM_THREADS=10 OMP_NESTED=TRUE ./ar
*/
#include <stdio.h>
#include <omp.h>
struct m10x1 {int v[10];};
int A [] = {84, 30, 95, 94, 36, 73, 52, 23, 2, 13};
struct m10x1 S = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
int n,m=0;
void print_m10x1(struct m10x1 x){
int i;
for(i=0;i<10;i++) printf("%d ",x.v[i]);
printf("\n");
}
struct m10x1 add_m10x1(struct m10x1 x,struct m10x1 y){
struct m10x1 r ={{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
int i;
for (i=0;i<10;i++) r.v[i]=x.v[i]+y.v[i];
return r;
}
#pragma omp declare reduction(m10x1Add: struct m10x1: \
omp_out=add_m10x1(omp_out, omp_in)) initializer( \
omp_priv={{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} )
int main ()
{
#pragma omp parallel for reduction(m10x1Add: S)
for ( n=0 ; n<10 ; ++n )
{
for (m=0; m<=n; ++m){
S.v[n] += A[m];
}
}
print_m10x1(S);
}
这逐字遵循OpenMP 4.0 features 第 97 页上的复数减少示例。
虽然并行版本可以正常工作,但可能存在性能问题,我没有调查:
- add_m10x1 输入和输出按值传递。
- add_m10x1 中的循环是串行运行的。
所说的“性能问题”是我自己造成的,不介绍它们是完全直截了当的:
-
add_m10x1 的参数应该通过引用传递(通过 C 中的指针,C++ 中的引用)
-
add_m10x1 中的计算应该就地完成。
-
add_m10x1 应声明为无效并删除返回语句。结果通过第一个参数返回。
- 应相应修改 declare reduction 杂注,组合器应只是函数调用而不是赋值(v4.0 规范 p181 第 9,10 行)。
-
add_m10x1 中的 for 循环可以通过 omp parallel for pragma 并行化
- 应启用并行嵌套(例如通过 OMP_NESTED=TRUE)
那么代码的修改部分是:
void add_m10x1(struct m10x1 * x,struct m10x1 * y){
int i;
#pragma omp parallel for
for (i=0;i<10;i++) x->v[i] += y->v[i];
}
#pragma omp declare reduction(m10x1Add: struct m10x1: \
add_m10x1(&omp_out, &omp_in)) initializer( \
omp_priv={{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} )