openmp
Boucle parallélisme dans OpenMP
Recherche…
Paramètres
Clause | Paramètre |
---|---|
private | Liste de variables privées séparées par des virgules |
firstprivate | Comme private , mais initialisé à la valeur de la variable avant d'entrer dans la boucle |
lastprivate | Comme private , mais la variable obtiendra la valeur correspondant à la dernière itération de la boucle à la sortie |
reduction | Opérateur de réduction : liste séparée par des virgules des variables de réduction correspondantes |
schedule | static , dynamic , guided , auto ou runtime avec une taille de morceau optionnelle après un coma pour les 3 anciens |
collapse | Nombre de boucles parfaitement imbriquées à réduire et à paralléliser ensemble |
ordered | Indique que certaines parties de la boucle devront être conservées dans l'ordre (ces parties seront spécifiquement identifiées avec des clauses ordered dans le corps de la boucle) |
nowait | Supprimer la barrière implicite existante par défaut à la fin de la construction de boucle |
Remarques
La signification de la clause d' schedule
est la suivante:
-
static[,chunk]
: distribue statiquement (c'est-à-dire que la distribution est effectuée avant d'entrer dans la boucle) les itérations de boucle en lots de taille dechunk
dans un tour de rôle. Sichunk
n'est pas spécifié, alors les morceaux sont aussi uniformes que possible et chaque thread obtient au plus un d'entre eux. -
dynamic[,chunk]
: distribuez les itérations de boucle entre les threads par lots de taille dechunk
avec unechunk
premier arrivé, premier servi, jusqu'à ce qu'il ne reste aucun lot. S'il n'est pas spécifié, lechunk
est défini sur 1 -
guided[,chunk]
: commedynamic
mais avec des lots dont les tailles deviennent de plus en plus petites, jusqu'à 1 -
auto
: laisser le compilateur et / ou la bibliothèque d'exécution décider de ce qui convient le mieux -
runtime
: Reportez la décision à l'exécution au moyen de la variable d'environnementOMP_SCHEDULE
. Si, au moment de l'exécution, la variable d'environnement n'est pas définie, la planification par défaut sera utilisée
La valeur par défaut pour la schedule
est définie par l'implémentation . Dans de nombreux environnements, il est static
, mais peut également être dynamic
ou pourrait très bien être auto
. Par conséquent, veillez à ce que votre implémentation ne repose pas implicitement sur elle sans la définir explicitement.
Dans les exemples ci-dessus, nous avons utilisé la forme fusionnée parallel for
ou parallel do
. Cependant, la construction de boucle peut être utilisée sans la fusionner avec la directive parallel
, sous la forme d'une #pragma omp for [...]
ou !$omp do [...]
autonome dans une région parallel
.
Pour la version Fortran uniquement, la ou les variables d'index de boucle de la ou des boucles parallèles sont toujours private
par défaut. Il n'est donc pas nécessaire de les déclarer explicitement private
(bien que cela ne soit pas une erreur).
Pour les versions C et C ++, les index de boucle sont comme les autres variables. Par conséquent, si leur étendue s'étend en dehors de la ou des boucles parallélisées (c'est-à-dire si elles ne sont pas déclarées comme for ( int i = ...)
mais plutôt comme int i; ... for ( i = ... )
alors elles doivent être déclarés private
.
Exemple typique en 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;
}
Dans cet exemple, nous calculons simplement 1 million de cosinus et additionnons leurs valeurs en parallèle. Nous chronométrons également l'exécution pour voir si la parallélisation a un effet sur les performances. Enfin, comme nous mesurons le temps, nous devons nous assurer que le compilateur n'optimisera pas le travail que nous avons effectué, donc nous faisons semblant d’utiliser le résultat en le retournant.
Même exemple à 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
Ici encore, nous calculons et accumulons 1 million de cosinus. Nous chronométrons la boucle et pour éviter toute optimisation indésirable du compilateur, nous faisons semblant d'utiliser le résultat.
Compiler et exécuter les exemples
Sur une machine Linux à 8 cœurs utilisant GCC version 4.4, les codes C peuvent être compilés et exécutés de la manière suivante:
$ 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
Pour la version Fortran, cela donne:
$ 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
Ajout de deux vecteurs en utilisant OpenMP parallèle pour construire
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];
}
}
Cet exemple ajoute deux vecteurs ( A
et B
dans C
) en générant une équipe de threads (spécifiés par la variable d' OMP_NUM_THREADS
OMP_NUM_THREADS, par exemple) et en attribuant à chaque thread un morceau de travail (dans cet exemple, assigné statiquement à la schedule(static)
expression).
Voir la section des remarques concernant le caractère facultatif private(i)
.