Was ist der Unterschied in OpenMP zwischen:
#pragma omp parallel sections
{
#pragma omp section
{
fct1();
}
#pragma omp section
{
fct2();
}
}
und :
#pragma omp parallel
{
#pragma omp single
{
#pragma omp task
fct1();
#pragma omp task
fct2();
}
}
Ich bin mir nicht sicher, ob der zweite Code richtig ist …
Der Unterschied zwischen Aufgaben und Abschnitten liegt im Zeitrahmen, in dem der Code ausgeführt wird. Abschnitte sind innerhalb der eingeschlossen sections
konstruieren und (es sei denn, die nowait
-Klausel angegeben wurde) werden Threads sie nicht verlassen, bis alle Abschnitte ausgeführt wurden:
[ sections ]
Thread 0: -------< section 1 >---->*------
Thread 1: -------< section 2 >*------
Thread 2: ------------------------>*------
... *
Thread N-1: ---------------------->*------
Hier N
Threads begegnen a sections
Konstruieren Sie mit zwei Abschnitten, wobei der zweite mehr Zeit in Anspruch nimmt als der erste. Die ersten beiden Threads führen jeweils einen Abschnitt aus. Das andere N-2
Threads warten einfach an der impliziten Barriere am Ende des Abschnittskonstrukts (hier als *
).
Aufgaben werden wann immer möglich an den sogenannten Aufgabenplanungspunkten in die Warteschlange gestellt und ausgeführt. Unter bestimmten Bedingungen kann es der Laufzeit erlaubt sein, Tasks zwischen Threads zu verschieben, sogar mitten in ihrer Lebensdauer. Solche Aufgaben werden als ungebunden bezeichnet, und eine ungebundene Aufgabe kann mit der Ausführung in einem Thread beginnen und dann an einem Planungspunkt von der Laufzeit zu einem anderen Thread migriert werden.
Dennoch sind Aufgaben und Abschnitte in vielerlei Hinsicht ähnlich. Beispielsweise erzielen die folgenden beiden Codefragmente im Wesentlichen dasselbe Ergebnis:
// sections
...
#pragma omp sections
{
#pragma omp section
foo();
#pragma omp section
bar();
}
...
// tasks
...
#pragma omp single nowait
{
#pragma omp task
foo();
#pragma omp task
bar();
}
#pragma omp taskwait
...
taskwait
funktioniert sehr gerne barrier
aber für Aufgaben – es stellt sicher, dass der aktuelle Ausführungsfluss angehalten wird, bis alle Aufgaben in der Warteschlange ausgeführt wurden. Es ist ein Scheduling Point, dh es ermöglicht Threads, Aufgaben zu bearbeiten. Das single
construct wird benötigt, damit Tasks nur von einem Thread erstellt werden. Wenn es keine gäbe single
konstruieren, würde jede Aufgabe erstellt werden num_threads
Zeiten, die vielleicht nicht das sind, was man will. Das nowait
Klausel in der single
Konstrukt weist die anderen Threads an, nicht bis zum zu warten single
Konstrukt ausgeführt wurde (dh entfernt die implizite Barriere am Ende der single
konstruieren). Also schlugen sie zu taskwait
sofort und beginnen Sie mit der Bearbeitung von Aufgaben.
taskwait
ist ein expliziter Planungspunkt, der hier zur Verdeutlichung gezeigt wird. Es gibt auch implizite Planungspunkte, insbesondere innerhalb der Barrierensynchronisation, egal ob explizit oder implizit. Daher könnte der obige Code auch einfach geschrieben werden als:
// tasks
...
#pragma omp single
{
#pragma omp task
foo();
#pragma omp task
bar();
}
...
Hier ist ein mögliches Szenario, was passieren könnte, wenn es drei Threads gibt:
+--+-->[ task queue ]--+
| | |
| | +-----------+
| | |
Thread 0: --< single >-| v |-----
Thread 1: -------->|< foo() >|-----
Thread 2: -------->|< bar() >|-----
Zeigen Sie hier innerhalb der | ... |
ist die Aktion des Planungspunkts (entweder die taskwait
Richtlinie oder das implizite Hindernis). Grundsätzlich Gewinde 1
und 2
Unterbrechen Sie, was sie zu diesem Zeitpunkt tun, und beginnen Sie mit der Verarbeitung von Aufgaben aus der Warteschlange. Sobald alle Aufgaben verarbeitet wurden, nehmen Threads ihren normalen Ausführungsfluss wieder auf. Beachten Sie, dass Fäden 1
und 2
könnte den Scheduling-Punkt vor dem Thread erreichen 0
hat die verlassen single
konstruieren, also links |
s müssen nicht unbedingt ausgerichtet sein (dies ist im Diagramm oben dargestellt).
Es könnte auch passieren, dass Thread 1
ist in der Lage, die Verarbeitung abzuschließen foo()
Aufgabe und fordern eine andere an, noch bevor die anderen Threads Aufgaben anfordern können. Also beides foo()
und bar()
könnte von demselben Thread ausgeführt werden:
+--+-->[ task queue ]--+
| | |
| | +------------+
| | |
Thread 0: --< single >-| v |---
Thread 1: --------->|< foo() >< bar() >|---
Thread 2: --------------------->| |---
Es ist auch möglich, dass der ausgewählte Thread die zweite Aufgabe ausführt, wenn Thread 2 zu spät kommt:
+--+-->[ task queue ]--+
| | |
| | +------------+
| | |
Thread 0: --< single >-| v < bar() >|---
Thread 1: --------->|< foo() > |---
Thread 2: ----------------->| |---
In einigen Fällen kann der Compiler oder die OpenMP-Laufzeitumgebung die Aufgabenwarteschlange sogar vollständig umgehen und die Aufgaben seriell ausführen:
Thread 0: --< single: foo(); bar() >*---
Thread 1: ------------------------->*---
Thread 2: ------------------------->*---
Wenn im Code der Region keine Aufgabenplanungspunkte vorhanden sind, kann die OpenMP-Laufzeit die Aufgaben starten, wann immer sie dies für angemessen hält. Zum Beispiel ist es möglich, dass alle Aufgaben bis zur Sperre am Ende des zurückgestellt werden parallel
Region erreicht ist.
Ich bin kein Experte für OpenMP, habe aber versucht, die Fib-Sequenz auf meinem Computer mit beiden zu testen task
und sections
Abschnitte
int fib(int n)
{
int i, j;
if (n < 2)
return n;
else
{
#pragma omp parallel sections
{
#pragma omp section
{
i = fib(n - 1);
}
#pragma omp section
{
j = fib(n - 2);
}
}
printf("Current int %d is on thread %d \n", i + j, omp_get_thread_num());
return i + j;
}
}
int main()
{
int n = 10;
#pragma omp parallel shared(n) {
#pragma omp single {
printf("%d\n", omp_get_num_threads());
printf("fib(%d) = %d\n", n, fib(n));
}
}
}
Aufgabe
#include <stdio.h>
#include <omp.h>
int fib(int n)
{
int i, j;
if (n<2)
return n;
else
{
#pragma omp task shared(i) firstprivate(n)
i=fib(n-1);
#pragma omp task shared(j) firstprivate(n)
j=fib(n-2);
#pragma omp taskwait
printf("Current int %d is on thread %d \n", i + j, omp_get_thread_num());
return i+j;
}
}
int main()
{
int n = 10;
#pragma omp parallel shared(n)
{
#pragma omp single
{
printf("%d\n", omp_get_num_threads());
printf ("fib(%d) = %d\n", n, fib(n));
}
}
}
Ergebnis für Abschnitte:
12
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 5 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 8 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 5 is on thread 0
Current int 13 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 5 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 8 is on thread 0
Current int 21 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 5 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 8 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 5 is on thread 0
Current int 13 is on thread 0
Current int 34 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 5 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 8 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 5 is on thread 0
Current int 13 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 5 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 1 is on thread 0
Current int 3 is on thread 0
Current int 8 is on thread 0
Current int 21 is on thread 0
Current int 55 is on thread 4
fib(10) = 55
Ergebnis für Aufgabe:
12
Current int 1 is on thread 3
Current int 2 is on thread 3
Current int 1 is on thread 8
Current int 2 is on thread 8
Current int 1 is on thread 8
Current int 1 is on thread 4
Current int 1 is on thread 11
Current int 1 is on thread 11
Current int 2 is on thread 11
Current int 3 is on thread 11
Current int 1 is on thread 11
Current int 2 is on thread 11
Current int 1 is on thread 11
Current int 1 is on thread 11
Current int 2 is on thread 11
Current int 3 is on thread 11
Current int 1 is on thread 11
Current int 2 is on thread 11
Current int 1 is on thread 11
Current int 1 is on thread 11
Current int 2 is on thread 11
Current int 3 is on thread 11
Current int 5 is on thread 11
Current int 8 is on thread 11
Current int 1 is on thread 8
Current int 2 is on thread 8
Current int 3 is on thread 8
Current int 5 is on thread 8
Current int 13 is on thread 8
Current int 1 is on thread 7
Current int 2 is on thread 7
Current int 1 is on thread 7
Current int 1 is on thread 7
Current int 1 is on thread 0
Current int 1 is on thread 0
Current int 2 is on thread 0
Current int 3 is on thread 0
Current int 1 is on thread 1
Current int 1 is on thread 6
Current int 2 is on thread 6
Current int 1 is on thread 9
Current int 2 is on thread 9
Current int 1 is on thread 2
Current int 2 is on thread 7
Current int 3 is on thread 7
Current int 5 is on thread 7
Current int 2 is on thread 5
Current int 5 is on thread 5
Current int 1 is on thread 5
Current int 2 is on thread 5
Current int 1 is on thread 5
Current int 1 is on thread 5
Current int 2 is on thread 5
Current int 3 is on thread 5
Current int 1 is on thread 5
Current int 2 is on thread 5
Current int 1 is on thread 5
Current int 1 is on thread 5
Current int 2 is on thread 5
Current int 3 is on thread 5
Current int 5 is on thread 5
Current int 1 is on thread 5
Current int 2 is on thread 5
Current int 1 is on thread 11
Current int 2 is on thread 11
Current int 1 is on thread 8
Current int 2 is on thread 8
Current int 5 is on thread 8
Current int 3 is on thread 1
Current int 8 is on thread 1
Current int 21 is on thread 1
Current int 1 is on thread 10
Current int 3 is on thread 10
Current int 8 is on thread 0
Current int 1 is on thread 4
Current int 3 is on thread 4
Current int 1 is on thread 9
Current int 3 is on thread 9
Current int 8 is on thread 9
Current int 3 is on thread 2
Current int 5 is on thread 3
Current int 13 is on thread 3
Current int 5 is on thread 6
Current int 13 is on thread 7
Current int 8 is on thread 10
Current int 21 is on thread 10
Current int 34 is on thread 3
Current int 55 is on thread 1
fib(10) = 55
Es scheint, dass Aufgaben viel klüger sind als Abschnitte, während Rechenressourcen verteilt werden
—————————–BEARBEITEN——————– ———
Für Leute, die nach Antworten auf diese Frage suchen, lesen Sie bitte den Kommentar unter diesem Beitrag.
Außer vermisst
;
Am Ende beider Anweisungen ist der zweite Code korrekt.– Christo Iljew
9. Dezember 2012 um 16:09 Uhr