2017-11-17 80 views
1

多くの反復を実行するコードがあり、条件が満たされた場合にのみ、反復の結果が保存されます。これは自然にwhileループとして表現されます。私は、各実現が独立しているので、コードを並列に実行しようとしています。 、OpenMP whileループ

while(nit<avit){ 
    #pragma omp parallel shared(nit,avit) 
    { 
     //do some stuff 
     if(condition){ 
      #pragma omp critical 
      { 
       nit++; 
       \\save results 
      } 
     } 
    }//implicit barrier here 
} 

と、これは正常に動作します...しかし、私は、並列ブロック内でやっているものは他よりも一回の反復に長い時間がかかる場合ことを意味し、それぞれ実現後の障壁が、そこにある:だから私はこれを持っていますすべてのスレッドは、次の繰り返しを続行するのではなく、完了を待っています。

スレッドが機能し続けるように、この障壁を回避する方法はありますか?私は何千もの反復を平均しているので、もう少し傷ついてはいけません(nit変数が既に実行中のスレッドでインクリメントされていない場合)...

私はこれを並列にしましたが、 forループの自動増分は、nit変数をワイルドにします。これは私の試みです:

#pragma omp parallel shared(nit,avit) 
{ 
    #pragma omp for 
    for(nit=0;nit<avit;nit++){ 
     //do some stuff 
     if(condition){ 
      \\save results 
     } else { 
      #pragma omp critical 
      { 
       nit--; 
      } 
     } 
    } 
} 

、もう1つは別のスレッドによってそれの増減から期待できるとして、それは...働くとループの周りに行く、予想通り、私nit変数が予測不可能な値をとり続けます異なる時に。

私はforループのブランクで増分を残しても試してみましたが、それはコンパイルできない、または自分のコードをだまししようとするためのループに増分を持たないために、

... 
incr=0; 
for(nit=0;nit<avit;nit+=incr) 
... 

が、その後、私のようなコードがクラッシュする...

アイデア?

おかげ

編集:ここでは、whileループのコードの作業を最小限の例です:

#include <random> 
#include <vector> 
#include <iostream> 
#include <time.h> 
#include <omp.h> 
#include <stdlib.h> 
#include <unistd.h> 

using namespace std; 

int main(){ 

    int nit,dit,avit=100,t,j,tmax=100,jmax=10; 
    vector<double> Res(10),avRes(10); 

    nit=0; dit=0; 
    while(nit<avit){ 
     #pragma omp parallel shared(tmax,nit,jmax,avRes,avit,dit) private(t,j) firstprivate(Res) 
     { 
      srand(int(time(NULL))^omp_get_thread_num()); 
      t=0; j=0; 
      while(t<tmax&&j<jmax){ 
       Res[j]=rand() % 10; 
       t+=Res[j]; 
       if(omp_get_thread_num()==5){ 
        usleep(100000); 
       } 
       j++; 
      } 
      if(t<tmax){ 
       #pragma omp critical 
       { 
        nit++; 
        for(j=0;j<jmax;j++){ 
         avRes[j]+=Res[j]; 
        } 
        for(j=0;j<jmax;j++){ 
         cout<<avRes[j]/nit<<"\t"; 
        } 
        cout<<" \t nit="<<nit<<"\t thread: "<<omp_get_thread_num(); 
        cout<<endl; 
       } 
      } else{ 
       #pragma omp critical 
       { 
        dit++; 
        cout<<"Discarded: "<<dit<<"\r"<<flush; 
       } 
      } 
     } 
    } 
    return 0; 
} 

私は他の人より長いを取って一つのスレッドをシミュレートするusleepパーツを追加しました。プログラムを実行すると、すべてのスレッドはスレッド5が完了するのを待たなければならず、次の実行を開始します。私がしようとしているのは、このような待ち時間を避けることです。つまり、他のスレッドが5を待たずに次の繰り返しを選択したいと思っています。

+0

*いくつかのものをより具体的に理解することなく、質問に正しく答えることは実際には不可能です。特に、何らかのものに* nitがアクセスされているかどうかと、複数のスレッドが同時に条件を持っているときに起こるはずのものや、あるスレッドが何らかのものをやっている間に複数回更新されたもの* ...それは難しいですが、良い、具体的な答えのためには、[mcve]を作成する必要があります。 – Zulan

+0

コメントありがとう@Zulan。最後に最小限の作業例を追加するために質問を編集しました。これが事を明確にすることを願っています。 –

答えて

0

あなたは基本的にavResが並列に書き込まれないことを確実にするためにわずかな変化で、this questionと同じ考え方に従うことができます:

int nit = 0; 
#pragma omp parallel 
while(1) { 
    int local_nit; 
    #pragma omp atomic read 
    local_nit = nit; 
    if (local_nit >= avit) { 
      break; 
    } 

    [...] 

    if (...) { 
      #pragma omp critical 
      { 
       #pragma omp atomic capture 
       local_nit = ++nit; 
       for(j=0;j<jmax;j++){ 
        avRes[j] += Res[j]; 
       } 
       for(j=0;j<jmax;j++){ 
        // technically you could also use `nit` directly since 
        // now `nit` is only modified within this critical section 
        cout<<avRes[j]/local_nit<<"\t"; 
       } 
      } 
    } else { 
      #pragma omp atomic update 
      dit++; 
    } 
} 

また、重要な領域で動作しますが、アトミックは、より効率的です。

考慮すべきもう1つのことがあります。rand()を並列コンテキストで使用しないでください。 this questionを参照してください。 C++の場合、<random>のプライベート(つまり、並列領域内で定義された)乱数ジェネレータを使用します。

+0

ありがとうございました@ Zulan、少し修正して、これは(ほぼ)トリックをやっているようです!私は 'atomic'にあまり精通していませんが、' critical 'のようにコードのブロックを追加することはできません...すべてのスレッドが結果を書くことができ、ちょうど最後に...もし私が '#pragma omp critical {local_nit = nit ++; for(j = 0; j

+0

「原子」は単一行の命令にしか使用できないことに気をつけてください。したがって、最終的なコードは書くための 'critical'セクションを持たなければなりません。最終的な訂正と同様に、 'local_nit = nit ++;'は '#pragma omp atomic read local_nit = nit;'で 'local_nit'が上に更新されているので' nit ++ '終わりが望まれなくなるまで書いてはいけません...あなたの助けをもう一度ありがとう。 –

+0

私のコードは、間違ってプレフィックスインクリメントの代わりにpostfixを使用していました。私はそれとリンクを修正しました。正しい形式の 'local_nit = ++ nit; if(local_nit == avit)... 'あなたは高価なクリティカルセクションなしで出力の書き込みを保護することができます。 – Zulan