Funktionsabhängigkeit openMP
Ich habe 5 Funktionen A, B, C, D, E.
Um DI auszuführen, müssen B, C ausgeführt werden, um EI auszuführen, müssen A, D ausgeführt werden.
Ich habe es versucht
int main()
{
#pragma omp parallel num_threads(5)
{
long t1 = clock();
int a = 0, b = 0, c = 0, d = 0, e = 0;
int th = omp_get_thread_num();
if (th == 0) {
a += A();
printf("A is finished after %d\n", clock() - t1);
}
if (th == 1) {
b += B();
printf("B is finished after %d\n", clock() - t1);
}
if (th == 2) {
c += C();
printf("C is finished after %d\n", clock() - t1);
}
if (th == 3 && (b == 1 && c == 1)) {
d += D();
printf("D is finished after %d\n", clock() - t1);
}
if (th == 4 && (a == 1 && d == 1)) {
e += E();
printf("E is finished after %d\n", clock() - t1);
}
}
return 0;
}
aber D, E haben nicht ausgeführt
Alle diese Funktionen geben bisher 1 zu Debugging-Zwecken zurück
Antworten
Die Variablen a, b, cund d,kann nicht verwendet werden , um die Kommunikation zwischen Threads , weil sie alle sind privat . Daher hat jeder Thread seine eigene private Kopie davon. Darüber hinaus ist es normalerweise keine gute Idee, sie für Synchronisationszwecke zu verwenden.
In Ihrem Code thread=3würde nie warten, if (th == 3 && (b == 1 && c == 1))weil:
bundcsind privat, sothread=3hatb=0undc=0unabhängig davon, was die anderen Threads mit ihren Kopien der Variablenb=0und gemacht habenc=0.- Es gibt nichts , was diesen Thread zum Warten auffordert ( z. B. einen Synchronisationskonstruktor).
Wenn Sie möchten, dass Threads aufeinander warten, verwenden Sie omp barrierstattdessen die Option. Alle Threads müssen die Barriere aufrufen, bevor sie mit der Berechnung fortfahren können.
int main()
{
#pragma omp parallel num_threads(5)
{
long t1 = clock();
int a = 0, b = 0, c = 0, d = 0, e = 0;
int th = omp_get_thread_num();
if (th == 0) {
a += A();
printf("A is finished after %d\n", clock() - t1);
}
if (th == 1) {
b += B();
printf("B is finished after %d\n", clock() - t1);
}
if (th == 2) {
c += C();
printf("C is finished after %d\n", clock() - t1);
}
// Threads will wait for each other here
#pragma omp barrier
if (th == 3) {
d += D();
printf("D is finished after %d\n", clock() - t1);
}
// Threads will wait for each other here
#pragma omp barrier
if (th == 4) {
e += E();
printf("E is finished after %d\n", clock() - t1);
}
}
return 0;
}
Ein komplexerer Ansatz wäre die Verwendung von Aufgaben mit Abhängigkeiten, eine Funktion, die im OpenMP 4.0-Standard veröffentlicht wurde. Es gibt bereits eine nette Erklärung, wie diese Funktion in diesem Thread funktioniert .
int a = 0, b = 0, c = 0, d = 0, e = 0;
#pragma omp parallel num_threads(5) shared(a, b, c, d)
{
#pragma omp single nowait
{
long t1 = clock();
int th = omp_get_thread_num();
#pragma omp task depend (out:a)
{
a += A();
printf("A is finished after %d\n", clock() - t1);
}
#pragma omp task depend (out:b)
{
b += B();
printf("B is finished after %d\n", clock() - t1);
}
#pragma omp task depend (out:c)
{
c += C();
printf("C is finished after %d\n", clock() - t1);
}
#pragma omp task depend (in:a, b) depend(out:d)
{
d += D();
printf("D is finished after %d\n", clock() - t1);
}
#pragma omp task depend (in:a, b)
{
e += E();
printf("E is finished after %d\n", clock() - t1);
}
}
}
return 0;
}
Eine geeignete OpenMP-Lösung wäre die Verwendung von Aufgaben mit Datenabhängigkeiten:
#pragma omp parallel num_threads(3)
#pragma omp single
{
double t1 = omp_wtime();
int a = 0, b = 0, c = 0, d = 0, e = 0;
#pragma omp task shared(a) depend(out: a)
{
a += A();
printf("A is finished after %f\n", omp_wtime() - t1);
}
#pragma omp task shared(b) depend(out: b)
{
b += B();
printf("B is finished after %f\n", omp_wtime() - t1);
}
#pragma omp task shared(c) depend(out: c)
{
c += C();
printf("C is finished after %f\n", omp_wtime() - t1);
}
#pragma omp task shared(b,c,d) depend(in: b,c) depend(out: d)
{
d += D();
printf("D is finished after %f\n", omp_wtime() - t1);
}
#pragma omp task shared(a,d,e) depend(in: a,d)
{
e += E();
printf("E is finished after %f\n", omp_wtime() - t1);
}
}
Hier wird die Aufgabe Aals Produzent für den Wert von amit depend(out: a)und die Aufgabe Dals Produzent von dmit markiert depend(out: d). Die Aufgabe Ewird als Verbraucher dieser beiden Werte mit markiert depend(in: a,d). Nach den Abhängigkeiten von Ausgabe (Produzent) und Eingabe (Konsument) erstellt die OpenMP-Laufzeit eine Ausführungs-DAG (gerichtetes azyklisches Diagramm), die die richtige Reihenfolge für die Ausführung aller Aufgaben angibt. Sie brauchen auch keine fünf Threads - drei sind genug.
Der Tasking-Code in einem singleKonstrukt ist OpenMP sehr idiomatisch.
Aufgabenabhängigkeiten wurden bereits 2013 von OpenMP 4.0 eingeführt, daher sollte jeder moderne Compiler außer MSVC ++ diese Funktion unterstützen.