2011-01-25 30 views
1

次のCコードでは、入れ子になったループでOpenMPを使用しています。競合状態が発生するので、私は最後にアトミック操作を実行します:コンパイラが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:私は同じ問題を与え、ガレスピーの考え方に基づいて新しいコード:

#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); 
} 

答えて

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

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

  2. 私はあなたが知っていると確信しているが、あなたはOMP並列に使用する場合は念のために、あなたの例では、reduction

  3. で処理する必要があり、デフォルトでは共有されるすべての変数のためであります。これはあなたが望むものではありません。たとえば、各スレッドは異なる値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]; 
        } 
    } 
    
+0

こんにちは、2)はい、私は削減前に使用しましたが、同じ問題です! 3)それで、還元も原子的助けもここでは何も起こっていないようです。 – flow

+0

@Werner:私がアトミックを使うとき、私はいつも正しい答えを得ます。私の答えに私のコードを追加します – csgillespie

+0

あなたのプログラムは効果的に動作します。以前のエラーは "a"と "b"を並列の内部で定義する必要があり、そのためにg ++コンパイラを使用する必要があることに気付きました。私がgccだけを使う前に。さて、私はあなたのコードを修正し、いくつかの新しいものを追加しました。あなたはPS2の質問の編集で見ることができるようになりましたが、今はg ++に対応した別の結果が得られます。私はあまりにも多くの操作をしているからですか? – flow

0

まず、実装に依存し、減少は、原子使用するよりも良いかもしれません。私は両方を試み、確かに見る時間をとるでしょう。

第二に、あなたがアトミックをオフのままならば、あなたはや人種に関連した問題(間違った結果を)表示されない場合があります。それはタイミングについてですが、ある実行から次の実行までは全く異なる可能性があります。私は、結果が間違っていた15万回の実行と1回だけ間違っていた場合を見てきました。

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

+0

はい、-Wall(申し訳ありませんが、私は完全に忘れてしまいました)では、プラグマoh_my_godでエラーが発生したときを見ることができます。アトムプラグマでエラーはありませんが、アトムプラガが実際に動作する保証は何ですか、私にとってはそれは役に立たないようです – flow

0

あなたはとてもだけ並列化のためのループになります

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

を持っています。

あなたが

#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 

をしなければならない 原子または縮小を持ちたい場合はそれ以外のすべてのものは、#の後にコメントします

0

私はこれは古い記事ですけど、私は問題があると思いますgcc、-fopenmpのパラメータの順序は、コンパイル行の最後でなければなりません。