openmp
Schleifenparallelität in OpenMP
Suche…
Parameter
Klausel | Parameter |
---|---|
private | Kommagetrennte Liste der privaten Variablen |
firstprivate | Wie private , aber vor dem Eintritt in die Schleife auf den Wert der Variablen initialisiert |
lastprivate | Wie private , aber die Variable erhält den Wert, der der letzten Iteration der Schleife beim Beenden entspricht |
reduction | Reduktionsoperator : Kommas getrennte Liste der entsprechenden Reduktionsvariablen |
schedule | static , dynamic , guided , auto oder runtime mit einer optionalen Chunk-Größe nach dem Koma für den 3-Former |
collapse | Anzahl perfekt geschachtelter Schleifen zum Zusammenfallen und Parallelisieren |
ordered | Sagt, dass einige Teile der Schleife in der richtigen Reihenfolge gehalten werden müssen (diese Teile werden durch einige ordered Klauseln innerhalb des Schleifenkörpers spezifisch gekennzeichnet) |
nowait | Entfernen Sie die implizite Barriere, die standardmäßig am Ende des Schleifenkonstrukts vorhanden ist |
Bemerkungen
Die Bedeutung der schedule
Klausel lautet wie folgt:
-
static[,chunk]
: Verteilen Sie statisch (dh die Verteilung erfolgt vor dem Eintritt in die Schleife) die Iterationen der Schleife in Batch-chunk
Größe in einem Round-Robin-Verfahren. Wennchunk
nicht angegeben wird, sind die Chunks so gleichmäßig wie möglich und jeder Thread erhält höchstens einen von ihnen. -
dynamic[,chunk]
: Verteilen Sie die Loop-Iterationen unter den Threads nach Chargen derchunk
mit einer First-Come-First-Served-Policy, bis keine Charge übrig bleibt. Wenn nicht angegeben, wirdchunk
auf 1 gesetzt -
guided[,chunk]
: Wiedynamic
aber mit Chargen, deren Größen immer kleiner werden (bis 1) -
auto
: Lass den Compiler und / oder die Laufzeitbibliothek entscheiden, was am besten geeignet ist -
runtime
: Verschieben Sie die Entscheidung zur Laufzeit anhand der UmgebungsvariablenOMP_SCHEDULE
. Wenn zur Laufzeit die Umgebungsvariable nicht definiert ist, wird die Standardplanung verwendet
Der Standard für den schedule
ist die Implementierungsdefinition . In vielen Umgebungen ist es static
, kann aber auch dynamic
oder sehr wohl auto
. Achten Sie daher darauf, dass Ihre Implementierung nicht implizit auf sie angewiesen ist, ohne sie explizit festzulegen.
In den obigen Beispielen haben wir die verschmolzene Form parallel for
oder parallel do
. Das Schleifenkonstrukt kann jedoch verwendet werden, ohne es mit der parallel
Direktive in Form einer #pragma omp for [...]
oder !$omp do [...]
Standalone-Direktive innerhalb einer parallel
Region zu !$omp do [...]
.
Nur für die Fortran-Version ist (sind) die Schleifenindexvariable (n) der parallelisierten Schleifen standardmäßig immer private
. Sie müssen daher nicht explizit als private
deklariert werden (dies ist jedoch kein Fehler).
Für die C- und C ++ - Version sind die Schleifenindizes wie alle anderen Variablen. Wenn sich ihr Gültigkeitsbereich außerhalb der parallelisierten Schleife (s) erstreckt (dh, wenn sie nicht wie for ( int i = ...)
sondern eher als int i; ... for ( i = ... )
deklariert sind int i; ... for ( i = ... )
dann sind sie dies müssen als private
deklariert werden.
Typisches Beispiel in C
#include <stdio.h>
#include <math.h>
#include <omp.h>
#define N 1000000
int main() {
double sum = 0;
double tbegin = omp_get_wtime();
#pragma omp parallel for reduction( +: sum )
for ( int i = 0; i < N; i++ ) {
sum += cos( i );
}
double wtime = omp_get_wtime() - tbegin;
printf( "Computing %d cosines and summing them with %d threads took %fs\n",
N, omp_get_max_threads(), wtime );
return sum;
}
In diesem Beispiel berechnen wir nur 1 Million Cosinus und summieren ihre Werte parallel. Wir prüfen die Ausführung auch, um zu sehen, ob die Parallelisierung Auswirkungen auf die Leistung hat. Da wir die Zeit messen, müssen wir schließlich sicherstellen, dass der Compiler die von uns geleistete Arbeit nicht optimiert. Wir geben also vor, das Ergebnis zu verwenden, indem wir es zurückgeben.
Dasselbe Beispiel in Fortran
program typical_loop
use omp_lib
implicit none
integer, parameter :: N = 1000000, kd = kind( 1.d0 )
real( kind = kd ) :: sum, tbegin, wtime
integer :: i
sum = 0
tbegin = omp_get_wtime()
!$omp parallel do reduction( +: sum )
do i = 1, N
sum = sum + cos( 1.d0 * i )
end do
!$omp end parallel do
wtime = omp_get_wtime() - tbegin
print "( 'Computing ', i7, ' cosines and summing them with ', i2, &
& ' threads took ', f6.4,'s' )", N, omp_get_max_threads(), wtime
if ( sum > N ) then
print *, "we only pretend using sum"
end if
end program typical_loop
Hier berechnen und berechnen wir erneut 1 Million Cosinus. Wir setzen die Schleife zeitlich fest, und um unerwünschte Compiler-Optimierungen zu vermeiden, geben wir vor, das Ergebnis zu verwenden.
Beispiele kompilieren und ausführen
Auf einem 8-Cores-Linux-Computer mit GCC-Version 4.4 können die C-Codes folgendermaßen kompiliert und ausgeführt werden:
$ gcc -std=c99 -O3 -fopenmp loop.c -o loopc -lm
$ OMP_NUM_THREADS=1 ./loopc
Computing 1000000 cosines and summing them with 1 threads took 0.095832s
$ OMP_NUM_THREADS=2 ./loopc
Computing 1000000 cosines and summing them with 2 threads took 0.047637s
$ OMP_NUM_THREADS=4 ./loopc
Computing 1000000 cosines and summing them with 4 threads took 0.024498s
$ OMP_NUM_THREADS=8 ./loopc
Computing 1000000 cosines and summing them with 8 threads took 0.011785s
Für die Fortran-Version gibt es:
$ gfortran -O3 -fopenmp loop.f90 -o loopf
$ OMP_NUM_THREADS=1 ./loopf
Computing 1000000 cosines and summing them with 1 threads took 0.0915s
$ OMP_NUM_THREADS=2 ./loopf
Computing 1000000 cosines and summing them with 2 threads took 0.0472s
$ OMP_NUM_THREADS=4 ./loopf
Computing 1000000 cosines and summing them with 4 threads took 0.0236s
$ OMP_NUM_THREADS=8 ./loopf
Computing 1000000 cosines and summing them with 8 threads took 0.0118s
Addition zweier Vektoren mit OpenMP parallel zum Konstrukt
void parallelAddition (unsigned N, const double *A, const double *B, double *C)
{
unsigned i;
#pragma omp parallel for shared (A,B,C,N) private(i) schedule(static)
for (i = 0; i < N; ++i)
{
C[i] = A[i] + B[i];
}
}
In diesem Beispiel werden zwei Vektoren ( A
und B
in C
) eingefügt, indem ein Team von Threads (z. B. durch die OMP_NUM_THREADS
OMP_NUM_THREADS angegeben OMP_NUM_THREADS
aufgerufen wird und jedem Thread ein Arbeitsblock zugewiesen wird (in diesem Beispiel statisch durch den schedule(static)
zugewiesen Ausdruck).
Siehe Abschnitt "Anmerkungen" bezüglich der private(i)
Wahlmöglichkeit.