2017-11-17 1 views
1

Ich habe einen Code, der viele Iterationen ausführt und nur wenn eine Bedingung erfüllt ist, wird das Ergebnis der Iteration gespeichert. Dies wird natürlich als while-Schleife ausgedrückt. Ich versuche, den Code parallel laufen zu lassen, da jede Realisierung unabhängig ist. Also ich habe dies:OpenMP While-Schleife

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

und das funktioniert gut ... aber es gibt eine Barriere nach jeder Erkenntnis, was bedeutet, dass, wenn das, was ich in dem parallelen Block täte dauert länger in einer Iteration als die andere, Alle meine Threads warten darauf, dass sie beendet wird, anstatt mit der nächsten Iteration fortzufahren.

Gibt es eine Möglichkeit, diese Barriere zu vermeiden, damit die Gewinde weiter funktionieren? Ich bin im Durchschnitt Tausende von Iterationen, so ein paar mehr nicht weh tun (wenn die nit Variable in bereits laufenden Threads nicht erhöht wurde) ...

Ich habe versucht, dies in eine Parallele für, aber die Durch die automatische Erhöhung der for-Schleife wird die Variable nit deaktiviert. Dies ist mein Versuch:

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

und es hält zu arbeiten und um die for-Schleife gehen, wie erwartet, aber meine nit Variable nimmt unvorhersehbare Werte ... wie man es von der Zunahme und Abnahme der es durch verschiedene Threads erwarten könnte zu anderen Zeiten.

Ich habe auch versucht, den Zuwachs in der Lassen Schleife leer, aber es funktioniert nicht kompilieren, oder versuchen, meinen Code zu verleiten keinen Zuwachs in der for-Schleife zu haben, wie

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

aber dann meine Code stürzt ab ...

Irgendwelche Ideen?

Dank

Edit: Hier ist ein Arbeits minimal Beispiel für den Code auf einer while-Schleife:

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

Ich fügte den usleep Teil einen Thread zu simulieren, die länger als die andere. Wenn Sie das Programm ausführen, müssen alle Threads darauf warten, dass Thread 5 beendet wird, und dann starten sie den nächsten Lauf. was ich versuche zu tun, ist genau solche Wartezeiten zu vermeiden, d. h. ich möchte, dass die anderen Threads die nächste Iteration auswählen, ohne zu warten, bis die 5 fertig ist.

+0

Es ist nicht wirklich möglich, die Frage ohne ein genaueres Verständnis von * einigen Sachen * richtig zu beantworten. Vor allem haben wir keine Ahnung, ob 'nit' in * some stuff * aufgerufen wird und was passieren soll, wenn mehrere Threads 'condition' zur gleichen Zeit haben, oder 'nit' wurde mehrmals aktualisiert, während ein Thread * einige Sachen gemacht hat * ... Es ist schwer, aber für eine gute, spezifische Antwort müssen Sie ein [mcve] erstellen. – Zulan

+0

Danke für den Kommentar @Zulan. Ich habe die Frage bearbeitet, um am Ende ein minimales Arbeitsbeispiel hinzuzufügen. Hoffe das klärt die Dinge. –

Antwort

0

Sie können im Grunde das gleiche Konzept wie für this question, mit einer leichten Variation folgen, um sicherzustellen, dass avRes nicht parallel geschrieben:

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++; 
    } 
} 

Es funktioniert auch mit kritischen Bereichen, aber atomics sind effizienter.

Es gibt eine andere Sache, die Sie beachten sollten, rand() sollte nicht in parallelen Kontexten verwendet werden. Siehe this question. Verwenden Sie für C++ einen privaten Zufallszahlengenerator (d. H. In der Parallelregion definiert) von <random>.

+0

Danke @Zulan, mit kleinen Änderungen scheint das (fast) den Trick zu machen! Ich bin nicht sehr vertraut mit "atomaren", aber es würde mich nicht erlauben, einen Block von Code wie mit "kritisch" hinzuzufügen ... Ich brauche, dass alle Threads in der Lage sind, Ergebnisse zu schreiben, und dass sie öfter geschrieben werden als nur bei Das Ende ... Wenn ich '#pragma omp kritisch {local_nit = nit ++; für (j = 0; j

+0

Macht nichts, ich verstehe jetzt, dass "atomare" kann nur für einzelne Zeile Anweisungen verwendet werden. Also muss der endgültige Code den 'kritischen' Abschnitt zum Schreiben haben. Und als eine letzte Korrektur glaube ich, dass 'local_nit = nit ++;' einfach 'nit ++; 'sein sollte, da' local_nit' oben aktualisiert wird, in #pragma omp atomic read local_nit = nit; 'und das' if' das verhindert das Schreiben bis das Ende nicht gewollt ist ... Danke nochmal für deine Hilfe. –

+0

Mein Code verwendete fälschlicherweise postfix statt Präfixinkrement. Ich habe das und den Link behoben. Mit der korrekten Form 'local_nit = ++ nit; if (local_nit == avit) ... 'Sie können das Schreiben von Ausgaben ohne einen teuren kritischen Abschnitt schützen. – Zulan