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
, c
und 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=3
würde nie warten, if (th == 3 && (b == 1 && c == 1))
weil:
b
undc
sind privat, sothread=3
hatb=0
undc=0
unabhängig davon, was die anderen Threads mit ihren Kopien der Variablenb=0
und 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 barrier
stattdessen 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 A
als Produzent für den Wert von a
mit depend(out: a)
und die Aufgabe D
als Produzent von d
mit markiert depend(out: d)
. Die Aufgabe E
wird 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 single
Konstrukt 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.