1

次の C コードでは、ネストされたループで OpenMP を使用しています。競合状態が発生するため、最後にアトミック操作を実行したい:

double mysumallatomic() {

  double S2 = 0.;
  #pragma omp parallel for shared(S2)
  for(int a=0; a<128; a++){
    for(int b=0; b<128;b++){
      double myterm = (double)a*b;
      #pragma omp atomic
      S2 += myterm;
    }
  }
  return S2;
}

問題は#pragma omp atomic、プログラムの動作に影響を与えないことです。削除しても何も起こりません。に変更して#pragma oh_my_godもエラーになりません!

omp プラグマをチェックするときにコンパイラにもっと厳密にするように指示できるのか、最後の変更を行ったときにエラーが発生しないのはなぜなのか、ここで何が問題になっているのだろうか

PS:コンパイルには次を使用します:

gcc-4.2 -fopenmp main.c functions.c -o main_elec_gcc.exe

PS2:同じ問題を引き起こし、gillespie のアイデアに基づいた新しいコード:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <omp.h>
#include <math.h>

#define NRACK 64
#define NSTARS 1024

double mysumallatomic_serial(float rocks[NRACK][3], float moon[NSTARS][3],
                             float qr[NRACK],float ql[NSTARS]) {
  int j,i;
  float temp_div=0.,temp_sqrt=0.;
  float difx,dify,difz;
  float mod2x, mod2y, mod2z;
  double S2 = 0.;

  for(j=0; j<NRACK; j++){
    for(i=0; i<NSTARS;i++){     
      difx=rocks[j][0]-moon[i][0];
      dify=rocks[j][1]-moon[i][1];
      difz=rocks[j][2]-moon[i][2];
      mod2x=difx*difx;
      mod2y=dify*dify;
      mod2z=difz*difz;
      temp_sqrt=sqrt(mod2x+mod2y+mod2z);
      temp_div=1/temp_sqrt;
      S2 += ql[i]*temp_div*qr[j];       
    }
  }
  return S2;
}

double mysumallatomic(float rocks[NRACK][3], float moon[NSTARS][3], 
                      float qr[NRACK],float ql[NSTARS]) {
  float temp_div=0.,temp_sqrt=0.;
  float difx,dify,difz;
  float mod2x, mod2y, mod2z;
  double S2 = 0.;

  #pragma omp parallel for shared(S2)
  for(int j=0; j<NRACK; j++){
    for(int i=0; i<NSTARS;i++){
      difx=rocks[j][0]-moon[i][0];
      dify=rocks[j][1]-moon[i][1];
      difz=rocks[j][2]-moon[i][2];
      mod2x=difx*difx;
      mod2y=dify*dify;
      mod2z=difz*difz;
      temp_sqrt=sqrt(mod2x+mod2y+mod2z);
      temp_div=1/temp_sqrt;
      float myterm=ql[i]*temp_div*qr[j];    
      #pragma omp atomic
      S2 += myterm;
    }
  }
  return S2;
}
int main(int argc, char *argv[]) {
  float rocks[NRACK][3], moon[NSTARS][3];
  float qr[NRACK], ql[NSTARS];
  int i,j;

  for(j=0;j<NRACK;j++){
    rocks[j][0]=j;
    rocks[j][1]=j+1;
    rocks[j][2]=j+2;
    qr[j] = j*1e-4+1e-3;
    //qr[j] = 1;
  }

  for(i=0;i<NSTARS;i++){
    moon[i][0]=12000+i;
    moon[i][1]=12000+i+1;
    moon[i][2]=12000+i+2;
    ql[i] = i*1e-3 +1e-2 ;
    //ql[i] = 1 ;
  }
  printf(" serial: %f\n", mysumallatomic_serial(rocks,moon,qr,ql));
  printf(" openmp: %f\n", mysumallatomic(rocks,moon,qr,ql));
  return(0);
}
4

4 に答える 4

3
  1. フラグを使用すると、-Wallプラグマ エラーが強調表示されます。たとえば、スペルを間違えるatomicと、次の警告が表示されます。

    main.c:15: warning: ignoring #pragma omp atomic1

  2. 私はあなたが知っていると確信していますが、念のため、あなたの例はreduction

  3. omp parallel を使用する場合、デフォルトではすべての変数が共有されます。これは、あなたが望むものではありません。たとえば、各スレッドには異なる値がありますdifx。代わりに、ループは次のようになります。

    #pragma omp parallel for default(none),\
    private(difx, dify, difz, mod2x, mod2y, mod2z, temp_sqrt, temp_div, i, j),\
    shared(rocks, moon, ql, qr), reduction(+:S2)
    for(j=0; j<NRACK; j++){
      for(i=0; i<NSTARS;i++){
        difx=rocks[j][0]-moon[i][0];
        dify=rocks[j][1]-moon[i][1];
        difz=rocks[j][2]-moon[i][2];
        mod2x=difx*difx;
        mod2y=dify*dify;
        mod2z=difz*difz;
        temp_sqrt=sqrt(mod2x+mod2y+mod2z);
        temp_div=1/temp_sqrt;
        S2 += ql[i]*temp_div*qr[j];  
      }
    }
    
于 2011-01-25T13:25:33.013 に答える
1

これが古い投稿であることは知っていますが、問題は gcc のパラメーターの順序にある​​と思います。-fopenmp はコンパイル行の最後にある必要があります。

于 2017-01-05T04:26:07.060 に答える
0

まず、実装によっては、アトミックを使用するよりも削減の方が優れている場合があります。私は両方を試して、確実に見るように時間を計ります。

第二に、アトミックを省略した場合、レースに関連する問題(間違った結果)が表示される場合と表示されない場合があります。それはすべてタイミングに関するものであり、実行ごとにまったく異なる可能性があります。結果が15万回に1回だけ間違っている場合や、常に間違っている場合があります。

第三に、プラグマの背後にある考え方は、効果がない場合、ユーザーはプラグマについて知る必要がないということでした。それに加えて、Unix(およびその派生物)の哲学は、問題がない限り静かであるということです。そうは言っても、多くの実装にはある種のフラグがあり、ユーザーは何が起こっているのかわからなかったため、より多くの情報を得ることができます。-Wallをgccで試すことができます。少なくとも、oh_my_godプラグマに無視されるフラグを立てる必要があります。

于 2011-01-25T13:37:59.983 に答える
0

あなたが持っている

#pragma omp parallel for shared(S2)
  for(int a=0; a<128; a++){
   ....

したがって、唯一の並列化は for ループになります。

アトミックまたはリダクションが必要な場合は、行う必要があります

#pragma omp parallel 
{
 #pragma omp for shared(S2)
   for(int a=0; a<128; a++){
     for(int b=0; b<128;b++){
       double myterm = (double)a*b;
       #pragma omp atomic
        S2 += myterm;
     } // end of second for
   } // end of 1st for
} // end of parallel code
return S2;
} // end of function

それ以外の場合、# の後のすべてがコメントになります

于 2015-12-30T16:10:11.577 に答える