在OpenMp中减lessarrays
我试图并行化下面的程序,但不知道如何减less数组。 我知道这是不可能的,但有没有其他的select? 谢谢。(我在m上加了减法,但是想要怎么做呢?)
#include <iostream> #include <stdio.h> #include <time.h> #include <omp.h> using namespace std; int A [] = {84, 30, 95, 94, 36, 73, 52, 23, 2, 13}; int S [10]; int n,m=0; time_t start_time, end_time; int main () { start_time = time(NULL); #pragma omp parallel for private (m)reduction(+:m) for ( n=0 ; n<10 ; ++n ) { for (m=0; m<=n; ++m){ S[n] += A[m]; } } end_time = time(NULL); cout << end_time-start_time; }
是的,可以使用OpenMP进行数组缩减。 在Fortran中,它甚至为此构build。 在C / C ++中,你必须自己做。 这里有两种方法来做到这一点。
第一种方法为每个线程创buildS
私有版本,并行填充它们,然后在关键部分将它们合并到S
中(请参见下面的代码)。 第二种方法创build一个尺寸为10 * nthreads的数组。 并行填充此数组,然后将其合并到S
而不使用临界区。 第二种方法要复杂得多,如果你不小心,可能会有多caching问题,尤其是在多插槽系统上。 有关更多详细信息,请参阅此填充直方图(数组缩减)与OpenMP并行,而不使用临界区域
第一种方法
int A [] = {84, 30, 95, 94, 36, 73, 52, 23, 2, 13}; int S [10] = {0}; #pragma omp parallel { int S_private[10] = {0}; #pragma omp for for (int n=0 ; n<10 ; ++n ) { for (int m=0; m<=n; ++m){ S_private[n] += A[m]; } } #pragma omp critical { for(int n=0; n<10; ++n) { S[n] += S_private[n]; } } }
第二种方法
int A [] = {84, 30, 95, 94, 36, 73, 52, 23, 2, 13}; int S [10] = {0}; int *S_private; #pragma omp parallel { const int nthreads = omp_get_num_threads(); const int ithread = omp_get_thread_num(); #pragma omp single { S_private = new int[10*nthreads]; for(int i=0; i<(10*nthreads); i++) S_private[i] = 0; } #pragma omp for for (int n=0 ; n<10 ; ++n ) { for (int m=0; m<=n; ++m){ S_private[ithread*10+n] += A[m]; } } #pragma omp for for(int i=0; i<10; i++) { for(int t=0; t<nthreads; t++) { S[i] += S_private[10*t + i]; } } } delete[] S_private;
我有两个关于Zboson的回答的评论:
1.方法1当然是正确的,但是由于#pragma omp临界值 ,因此减less循环实际上是正确的,因为部分matrix对于每个线程是局部的,并且相应的减less必须由线程完成matrix。
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 ",xv[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++) rv[i]=xv[i]+yv[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){ Sv[n] += A[m]; } } print_m10x1(S); }
这遵循OpenMP 4.0function的第97页上的复数缩减示例。
虽然并行版本能够正常工作,但可能存在性能问题,我还没有调查:
- add_m10x1input和输出按值传递。
- add_m10x1中的循环是连续运行的。
所说的“performance问题”是我自己制作的,而不是直接介绍它们:
- 参数add_m10x1应该通过引用传递(通过C中的指针,C ++中的引用)
- add_m10x1中的计算应该完成。
- add_m10x1应该被声明为void,并且返回语句被删除。 结果通过第一个参数返回。
- 声明还原编译指示应相应修改,合并器应该只是一个函数调用而不是一个赋值(v4.0规范p181第9,10行)。
- add_m10x1中的for循环可以通过一个omp并行来编译并行
- 并行嵌套应该被启用(例如通过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}} )
如果将您的代码转换为可以在OpenMP简化操作中使用数组的Fortran,则不具有吸引力,您可以使用一堆临时variables。 例如
int S0, S1, S2, ..., S9; ... #pragma omp parallel for private(...) shared(S0, S1, S2, ..., S9) \ reduction(+:S0, S1, S2, ..., S9) for ...
这给你留下了一个不可预料的前景:必须写一些if
或case
语句来确定哪个临时对象被更新。 如果你的代码只是一个你想用来学习的例子,继续。
但如果你的意图是真正写一个平行前缀和程序,然后search。 这是一个很好的开始。