openmp
Równoległość pętli w OpenMP
Szukaj…
Parametry
Klauzula | Parametr |
---|---|
private | Rozdzielana przecinkami lista zmiennych prywatnych |
firstprivate | Jak private , ale inicjowany do wartości zmiennej przed wejściem do pętli |
lastprivate | Jak private , ale zmienna otrzyma wartość odpowiadającą ostatniej iteracji pętli przy wyjściu |
reduction | operator redukcji : rozdzielona przecinkami lista odpowiednich zmiennych redukcji |
schedule | static , dynamic , guided , auto lub runtime z opcjonalnym rozmiarem porcji po śpiączce dla 3 pierwszych |
collapse | Liczba idealnie zagnieżdżonych pętli do zwinięcia i równoległości |
ordered | Informuje, że niektóre części pętli będą musiały być utrzymywane w kolejności (części te zostaną konkretnie zidentyfikowane za pomocą niektórych ordered klauzul wewnątrz ciała pętli) |
nowait | Usuń domyślną barierę istniejącą domyślnie na końcu konstrukcji pętli |
Uwagi
Znaczenie klauzuli schedule
jest następujące:
-
static[,chunk]
: Dystrybucja statyczna (co oznacza, że dystrybucja odbywa się przed wejściem do pętli) iteracje pętli w partiach wielkościchunk
w sposób okrągły. Jeślichunk
nie jest określona, porcje są tak równe, jak to możliwe, a każdy wątek otrzymuje co najwyżej jeden z nich. -
dynamic[,chunk]
: Dystrybucja iteracji pętli wśród wątków o partiachchunk
wielkości z pierwszego-come-first-służył polityce, dopóki nie szczątków wsadowych. Jeśli nie zostanie określony,chunk
jest ustawiona na 1 -
guided[,chunk]
: jakdynamic
ale z partiami, których rozmiary stają się coraz mniejsze, do 1 -
auto
: Pozwól kompilatorowi i / lub bibliotece wykonawczej zdecydować, co najlepiej pasuje -
runtime
: decyzja zostanie podjęta w czasie wykonywania zaOMP_SCHEDULE
zmiennej środowiskowejOMP_SCHEDULE
. Jeśli w czasie wykonywania zmienna środowiskowa nie zostanie zdefiniowana, zostanie zastosowane domyślne planowanie
Domyślnym schedule
jest definiowanie implementacji . W wielu środowiskach jest static
, ale może być również dynamic
lub bardzo dobrze auto
. Dlatego uważaj, aby twoja implementacja nie polegała w sposób dorozumiany na nim bez wyraźnego ustawienia.
W powyższych przykładach zastosowaliśmy stopioną postać parallel for
lub parallel do
. Konstrukcji pętli można jednak używać bez łączenia jej z dyrektywą parallel
, w postaci #pragma omp for [...]
lub !$omp do [...]
samodzielnej dyrektywy w regionie parallel
.
Tylko w wersji Fortran zmienne indeksu pętli sparaliżowanej pętli są domyślnie zawsze private
. Dlatego nie ma potrzeby jawnego uznawania ich za private
(chociaż nie jest to błąd).
W przypadku wersji C i C ++ indeksy pętli są jak wszystkie inne zmienne. Dlatego jeśli ich zasięg wykracza poza równoległe pętle (pętle) (co oznacza, że nie są zadeklarowane jak for ( int i = ...)
ale raczej jak int i; ... for ( i = ... )
wówczas muszą zostać uznane za private
.
Typowy przykład w 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;
}
W tym przykładzie po prostu obliczamy 1 milion cosinusów i sumujemy ich wartości równolegle. Wykonujemy również czas wykonania, aby sprawdzić, czy równoległość ma jakikolwiek wpływ na wydajność. Wreszcie, ponieważ mierzymy czas, musimy upewnić się, że kompilator nie zoptymalizuje wykonanej pracy, więc udajemy, że wykorzystujemy wynik, po prostu zwracając go.
Ten sam przykład w 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
Tutaj ponownie obliczamy i gromadzimy 1 milion cosinusów. Mierzymy czas w pętli i aby uniknąć niepożądanej optymalizacji kompilatora, udajemy, że wykorzystujemy wynik.
Kompilowanie i uruchamianie przykładów
Na 8-rdzeniowym komputerze z systemem Linux używającym GCC w wersji 4.4, kody C można skompilować i uruchomić w następujący sposób:
$ 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
W przypadku wersji Fortran daje:
$ 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
Dodanie dwóch wektorów przy użyciu równoległego OpenMP do konstrukcji
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];
}
}
W tym przykładzie dodano dwa wektor ( A
i B
do C
), OMP_NUM_THREADS
zespół wątków (określony na OMP_NUM_THREADS
przez zmienną OMP_NUM_THREADS
OMP_NUM_THREADS) i przypisując każdemu wątkowi część pracy (w tym przykładzie przypisaną statycznie przez schedule(static)
wyrażenie).
Patrz sekcja uwag w odniesieniu do opcjonalności private(i)
.