3 回答

TA貢獻1871條經驗 獲得超13個贊
是的,可以使用OpenMP進行陣列縮減。在Fortran中,它甚至對此具有構造。在C / C ++中,您必須自己做。這有兩種方法。
第一種方法S為每個線程創建的私有版本,并行填充它們,然后將它們合并到S關鍵部分(請參見下面的代碼)。第二種方法創建一個具有10 * nthread個尺寸的數組。并行填充此數組,然后在S不使用關鍵節的情況下將其合并到其中。第二種方法要復雜得多,如果不小心,可能會出現緩存問題,尤其是在多插槽系統上。有關更多詳細信息,請參見此填充直方圖(數組縮減)與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;

TA貢獻1810條經驗 獲得超4個贊
關于Zboson的答案,我有兩點評論:
1.方法1當然是正確的,但是歸約循環實際上是串行運行的,因為#pragma ompcritical必不可少,因為部分線程對于每個線程都是局部的,并且相應的歸約具有通過線程來完成矩陣。
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功能第97頁的復數減少示例進行。
盡管并行版本可以正常工作,但可能存在性能問題,我尚未調查:
add_m10x1輸入和輸出按值傳遞。
add_m10x1中的循環按順序運行。
所說的“性能問題”是我自己造成的,完全不介紹它們就很簡單:
add_m10x1的參數應通過引用傳遞(通過C中的指針,C ++中的引用)
add_m10x1中的計算應就位。
應該將add_m10x1聲明為void,并刪除return語句。結果通過第一個參數返回。
應該相應地減少聲明減少編譯指示,合并器應該只是函數調用而不是賦值(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}} )
- 3 回答
- 0 關注
- 777 瀏覽
添加回答
舉報