Ceci est une ancienne révision du document !
Table des matières
Compilateur
Pour activer l'option : -openmp
pour GCC
, /openmp
pour Visual Studio (Projet
|Propriétés
|C/C++
|Langage
|Prise en charge de OpenMP
).
OpenMP Application Programming Interface (Norme OpenMP v4.5) Archive du 09/03/2019
Guide into OpenMP: Easy multithreading programming for C++ Archive du 09/03/2019
Généralités de #pragma
Définitions
- team : représente le groupe de threads utilisé par OpenMP.
Fonctions OpenMP
omp_get_thread_num()
: le nième thread de la team à partir de 0.omp_get_num_threads()
: nombre de threads dans la team.
#pragma omp for
Doit être placé juste avant une boucle for
.
La boucle va être décomposée en n blocs (n étant le nombre de threads du CPU). L'ordre d'exécution des blocs est séquentiel car l'instruction ne précise pas l'exécution en parallèle.
#pragma omp for for (int n = 0; n < 10; ++n) printf(" %d", n);
#pragma omp parallel
Doit être placé juste avant un bloc {}
.
#pragma omp parallel { for (int64_t i = 0; i < 4000000000; i++) {} }
Le code va s'exécute en parallèle. Avec cette seule instruction, la boucle for est exécutée 8*4000000000.
#pragma omp parallel for
Doit être placé juste avant une boucle for
.
Là, la boucle est découpée et exécutée en parallèle, ce qui est le but recherché.
#pragma omp parallel for for (int64_t i = 0; i < 4000000000; i++) {}
C'est l'équivalent au code ci-dessous :
#pragma omp parallel { #pragma omp for for (int64_t i = 0; i < 4000000000; i++) {} }
#pragma omp simd
Cela parallélise les instructions de calcul pour utiliser au mieux les SSE* et autres. On peut autant y faire des calculs flottants qu'en entier. Le fonctionnement est similaire aux calculs CUDA :
#pragma omp simd for(int n=0; n<size; ++n) sinTable[n] = std::sin(2 * M_PI * n / size);
Options
if
#pragma
est exécutée si la condition est vraie.
extern int parallelism_enabled; #pragma omp parallel for if(parallelism_enabled)
num_threads(X)
Indique le nombre de threads de la team.
#pragma omp parallel num_threads(3)
Gestion des variables
La variable est locale (private
) à chaque thread :
int n,m; #pragma omp parallel private(n) printf("%d %#010zx\n", omp_get_thread_num(), &n); printf("%#010zx\n", &m);
L'adresse de la variable n
est différente pour chaque thread.
Rendu :
4 0x7f341ff72e14 0 0x7ffd31aadaf4 7 0x7f341e76fe14 3 0x7f3420773e14 1 0x7f3421775e14 6 0x7f341ef70e14 5 0x7f341f771e14 2 0x7f3420f74e14 0x7ffd31aadb44
On voit qu'un bloc de taille 0x801000
est alloué pour que thread.
Taille des blocs
static
Par défaut, tous les threads s'occupent de la même taille de données (static
).
#pragma omp for schedule(static)
dynamic
Le thread interroge OpenMP pour savoir quelle plage de données il doit exécuter puis une fois terminée, il interroge OpenMP à nouveau.
Utile quand chaque élément dans la boucle n'a pas un temps d'exécution fixe ou quand on utilise ordered
pour éviter de bloquer les threads suivants.
La taille de chaque bloc peut aussi être imposée.
#pragma omp for schedule(dynamic, 3)
guided
C'est un mix entre static
et dynamic
.
runtime
C'est OpenMP qui décide à l'exécution quelle est la meilleure stratégie à appliquer.
monolithic
/nonmonolithic
monolithic : les blocs sont exécutés par ordre croissant, nonmonolithic : les blocs sont exécutés par ordre non forcément croissant,
<code c> #pragma omp for schedule(nonmonotonic:dynamic) </c>