2017-05-05 26 views
0

現在、内部ループに結果を保存する必要がある条件で、ループのセットを並列化する高速で信頼性の高い方法を探しています。 コードは3Dグリッド内の膨大なポイントを通過するはずです。このボリューム内のいくつかの点については、別の条件(角度のチェック)をチェックしなければならず、この条件が満たされれば密度を計算する必要があります。Openmpは順序付き出力付きのループをネストしました

これまでのところ、最も速い方法は、すべてのループの外側にある#pragma omp parallel for private (x,y,z) collapse(3)または最も大きなループであるだけでなく、CPUを集中的に呼び出す機能を呼び出す最も内側のループ(phiInd)の#pragma omp parallel forでした。

濃度値を内部ループ内の濃度に格納する必要があります。その後、濃度アレイは後に別々に保存される。 私の問題は今、私が設定したスレッドの数によっては、密度の配列が異なる結果になることです。シリアルバージョンと1スレッドだけを持つopenmpの実行結果は同じです。 スレッド数を増やすと、結果は同じポイントになりますが、その結果はシリアルバージョンとは異なります。

私は#pragma omp for orderedがあることを知っていますが、計算が遅すぎます。 ポイント(x、y、z)に基づいて結果を取得しながら、このループを並列化する方法はありますか? 多分もっと明確に:スレッド番号を増やすと結果が変わるのはなぜですか?

double phipoint, Rpoint, zpoint; 
double phiplane; 
double distphi = 2.0 * M_PI/nPlanes; //set desired distace to phi to assign point to fluxtubeplane 
double* densityarr = new double[max_x_steps * max_y_steps * max_z_steps]; 


for (z = 0; z < max_z_steps; z++) { 
    for (x = 0; x < max_x_steps; x++) { 
     for (y = 0; y < max_y_steps; y++) { 
      double x_center = x * stepSizeGrid - max_x/2; 
      double y_center = y * stepSizeGrid - max_y/2; 
      double z_center = z * stepSizeGrid - max_z/2; 
      cartesianCoordinate* pos = new cartesianCoordinate(x_center, y_center, z_center); 
      linearToroidalCoordinate* tor = linearToroidal(*pos); 
      simpleToroidalCoordinate* stc = simpleToroidal(*pos); 

      phipoint = tor->phi; 
      if (stc->r <= 0.174/*0.175*/) {//check if point is in vessel 

       for (int phiInd = 0; phiInd < nPlanes; ++phiInd) { 
        phiplane = phis[phiInd]; 

        if (abs(phipoint - phiplane) <= distphi) {//find right plane for point 
         Rpoint = tor->R; 
         zpoint = tor->z;     
         densityarr[z * max_y_steps * max_x_steps + x * max_y_steps + y] = TubePlanes[phiInd].getMinDistDensity(Rpoint, zpoint); 

        } 
       } 
      } 

      delete pos, tor, stc; 
     } 
    } 
} 
+0

ここで 'phipoint' /' phipane'が定義されています。 'densityarr'はどのような型ですか? – Zulan

+0

これらのループの前。 'phipoint'と' phipane'は両方とも二倍です。 'densityarr'はdouble *です。.. ' double phiplane、phipoint; double * densityarr = new double [max_x_steps * max_y_steps * max_z_steps]; ' – LeBo

答えて

0

まず、並列バージョンのエラーを解決する必要があります。競合条件は、共有変数phipoint(パラレル外側ループ)とphiplaneRpointzpoint(すべてのパラレルループ)に書き込むことです。それらをプライベートに宣言するか、より良いことに、それらを宣言して最初にローカルに宣言しなければなりません(暗黙的にプライベートにします)。そうすれば、コードは非常に簡単に推論できます。これは並列コードにとって非常に重要です。

あなたが記述したように外側のループを並列化することは明らかであり、最も効率的なアプローチです。重大な負荷の不均衡がある場合(stc->r <= 0.174がポイント間で均等に分散していない場合)、schedule(dynamic)を使用するとよいでしょう。

あなたのケースでは、内側ループを並列化する必要はないようです。一般に、外部ループは、十分な並行作業を公開していないか、競合条件を持っていないか、依存関係があるか、キャッシュの問題がない限り、オーバーヘッドが少ないため効率が向上します。しかし、それを試して測定する価値のある練習になるでしょう。ただし、複数のphisが条件を満たす場合は、densityarrに書き込む際に競合状態が発生することがあります。全体的にはループはちょっと奇妙に思えます - あなたはたった一つの結果しかdensityarrに入れていないので、ループを逆にして最初のものを見つけたら取り消すことができます。これはシリアル実行を大いに助けますが、並列化を妨げる可能性があります。また、条件を満たすφが見つからない場合、またはポイントが船舶内にない場合は、densityarrのそれぞれのエントリは初期化されずに残っています。これは、値が有効かどうかを後で判断できないため非常に危険です。ない。

一般的な注意点として、必要がない限りnewのオブジェクトを割り当てないでください。 posをスタックに置くだけで、パフォーマンスが向上します。 (並列)ループ内でメモリを割り当てることはパフォーマンス上の問題になる可能性がありますので、Toroidalの取得方法を考え直すことをおすすめします。

TubePlanes[phiInd].getMinDistDensityには副作用がないと仮定していますが、それ以外の場合は並列化に問題があります。

+0

あなたは私の週を保存しました!ありがとうございました!私は変数をローカルに宣言し、濃度ゼロを0で初期化しました。私のUpvotesはまだカウントされませんが、あなたの答えは私の問題を解決しました。 – LeBo

+0

@LeBo喜んで助けてください!答えの左側にあるチェックマークをクリックして、問題を解決済みとしてマークしてください。 – Zulan

関連する問題