parallel programming in c with mpi and openmp

76
Parallel Programming in C with MPI and OpenMP Michael J. Quinn Chapitre 17 Open MP

Upload: elsu

Post on 19-Mar-2016

58 views

Category:

Documents


0 download

DESCRIPTION

Parallel Programming in C with MPI and OpenMP. Chapitre 17. Open MP. Michael J. Quinn. OpenMP. OpenMP: Interface de programmation (API) pour le calcul parallèle sur architecture à mémoire partagée. Directives pour le compilateur Bibliothèque logicielle Variables de l ’ environnement - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Parallel Programming in C with MPI and OpenMP

Parallel Programmingin C with MPI and OpenMPMichael J. Quinn

Chapitre 17Open MP

Page 2: Parallel Programming in C with MPI and OpenMP

OpenMP OpenMP: Interface de programmation (API)

pour le calcul parallèle sur architecture à mémoire partagée. Directives pour le compilateur Bibliothèque logicielle Variables de l’environnement

OpenMP fonctionne avec Fortran, C, ou C++

Page 3: Parallel Programming in C with MPI and OpenMP

Modèle à mémoire partagée

P r o c es s o r P r o c es s o r P r o c es s o r P r o c es s o r

M em o r y

Les processeurs interagissent et se synchronisent à l’aide de variables partagées.

Page 4: Parallel Programming in C with MPI and OpenMP

Parallélisme avec Fork et Join

Initialement un seul thread est actif (maître) Le maître exécute le code séquentiel. Fork: Le maître crée ou active des threads

additionnels afin d’exécuter du code en parallèle.

Join: À la fin du code parallèle, les threads sont éliminés ou suspendus et le flot de contrôle retourne à l’unique thread maître.

Page 5: Parallel Programming in C with MPI and OpenMP

Parallélisme avec Fork et Join

Time

f o rk

jo in

M as ter T h r ead

fo rk

jo in

O th er th r ead s

Page 6: Parallel Programming in C with MPI and OpenMP

Parallélisation incrémentielle

Programme séquentiel: Cas particulier d’un programme parallèle à mémoire partagée.

Parallélisation incrémentielle: On transforme un programe séquentiel en programme parallèle de façon graduelle.

Le parallélisme incrémentiel est un avantage important de la programmation parallèle à mémoire partagée.

Page 7: Parallel Programming in C with MPI and OpenMP

Boucle for parallèle En C le parallélisme de données est souvent exprimé à l’aide de boucles for:

for (i = first; i < size; i += prime)

marked[i] = 1;

Avec OpenMP il est facile d’indiquer quand une boucle doit être exécuté en parallèle. Le compilateur se charge de transformer le code séquentiel en code parallèle:

création des threads affectation des itérations aux threads.

Page 8: Parallel Programming in C with MPI and OpenMP

Pragmas Pragma: Directive au compilateur C ou C++ Signifie “pragmatic information”

Permet au programmeur de communiquer avec le compilateur

Le compilateur est libre d’ignorer les directives

Syntaxe:#pragma omp <reste du pragma>

Page 9: Parallel Programming in C with MPI and OpenMP

Parallel for#pragma omp parallel for [clause [[,] clause …]

for (i = 0; i < N; i++)

a[i] = b[i] + c[i];

Le compilateur doit être en mesure de vérifier si le système d’exécution aura l’information nécessaire à l’ordonnancement des itérations de la boucle. Indépendance des itérations Nombre d’itérations

Page 10: Parallel Programming in C with MPI and OpenMP

Forme canonique d’une boucle

parallel for

Page 11: Parallel Programming in C with MPI and OpenMP

Variables privées et partagées

Variable partagée: Même adresse mémoire pour tous les threads

Variable privée: Différentes adresses mémoire pour différents threads.

Un thread ne peut pas accéder à une variable privée appartenant à un autre thread.

Par défaut, dans un “parallel for”, les variables sont partagées sauf l’indice de boucle.

Page 12: Parallel Programming in C with MPI and OpenMP

Variables privées et partagées

in t m ain ( in t a rg c , c h ar * ar g v [ ] ){

in t b [ 3 ] ; c h ar * c p tr ;

in t i;

c p tr = m allo c (1 ) ;# p r ag m a o m p p ara lle l f o r

f o r ( i = 0 ; i < 3 ; i+ + ) b [ i] = i;

Heap

S tac k

cptrb i

ii

M as te r Thre ad (Thre ad 0 )

Thre ad 1…}

Page 13: Parallel Programming in C with MPI and OpenMP

Comment le système sait-il combien de threads il faut

créer?Variable de l’environnement: OMP_NUM_THREADS4 fonctions utiles: omp_get_num_procs

omp_set_num_threads

omp_get_num_threads

omp_get_thread_num

Page 14: Parallel Programming in C with MPI and OpenMP

Fonction omp_get_num_procs

Retourne le nombre de processeurs (physique ou virtuels) disponibles par le programme parallèle.

int omp_get_num_procs (void)

Page 15: Parallel Programming in C with MPI and OpenMP

Fonction omp_set_num_threads

Le nombre de threads actifs dans les section de code parallèle sera égal au paramètre de la fonction

Peut être appelé à plusieurs endroits dans le programme.

void omp_set_num_threads (int t)

Page 16: Parallel Programming in C with MPI and OpenMP

Fonction omp_get_num_threads

Retourne le nombre de threads actifs.

int omp_get_num_threads (void)

Page 17: Parallel Programming in C with MPI and OpenMP

Fonction omp_get_thread_num

Retourne le numéro du thread.

int omp_get_thread_num(void)

Page 18: Parallel Programming in C with MPI and OpenMP

Déclarer des variables privées

Exemple: Algorithme de Floyd

for (i = 0; i < n; i++)

for (j = 0; j < n; j++)

a[i][j] = MIN(a[i][j],a[i][k]+a[k][j]);

N’importe laquelle des deux boucles peut être exécutée en parallèle (exécuter les deux en parallèle nécessite trop de threads) On préfère paralléliser la boucle extérieure pour minimiser le nombre de fork/join Chaque thread doit alors posséder sa propre copie de la variable j

Page 19: Parallel Programming in C with MPI and OpenMP

Clause “private” Clause: Composante optionnelle à un

pragma Clause “Private”: indique au compilateur de

créer une ou plusieurs variables privées.

private ( <variable list> )

Page 20: Parallel Programming in C with MPI and OpenMP

Exemple

#pragma omp parallel for private(j)for (i = 0; i < n; i++) for (j = 0; j < n; j++) a[i][j] = MIN(a[i][j],a[i][k]+a[k][j]);

Page 21: Parallel Programming in C with MPI and OpenMP

Clause “firstprivate” Pour créer une variable privée dont la valeur

initiale est identique à celle du thread maître avant d’entrée dans la boucle.

Les variable sont initialisées une seule fois pour chaque thread et non pas à chaque itération

La modification d’une valeur est effective aussi pour les autres itérations exécutées par un thread donné.

Page 22: Parallel Programming in C with MPI and OpenMP

Sections critiques

double area, pi, x;int i, n;...area = 0.0;for (i = 0; i < n; i++) { x += (i+0.5)/n; area += 4.0/(1.0 + x*x);}pi = area / n;

Exemple: Approximation de

Page 23: Parallel Programming in C with MPI and OpenMP

Condition de concurrence

Si on ne fait que paralléliser la boucle...

double area, pi, x;int i, n;...area = 0.0;#pragma omp parallel for private(x)for (i = 0; i < n; i++) { x = (i+0.5)/n; area += 4.0/(1.0 + x*x);}pi = area / n;

Page 24: Parallel Programming in C with MPI and OpenMP

Condition de concurrence

... On obtient une condition de concurrence pour modifier la variable area

T h r ead A T h r ead BValu e o f a r ea

1 1 .6 6 7+ 3 .7 6 5

+ 3 .5 6 3

1 1 .6 6 7

1 5 .4 3 2

1 5 .2 3 0

Page 25: Parallel Programming in C with MPI and OpenMP

Pragma “critical” Section critique: portion de code qui ne

peut être exécuté que par un seul thread à la fois.

On met

#pragma omp critical

devant le bloc de code C.

Page 26: Parallel Programming in C with MPI and OpenMP

Exempledouble area, pi, x;int i, n;...area = 0.0;#pragma omp parallel for private(x)for (i = 0; i < n; i++) { x = (i+0.5)/n;#pragma omp critical area += 4.0/(1.0 + x*x);}pi = area / n;

Correct mais inefficace!

Page 27: Parallel Programming in C with MPI and OpenMP

Réductions Une réduction est l’application d’une opération

associative sur les éléments d’un vecteur Les réductions sont si courantes que OpenMP

fourni un mécanisme facilitant son application. On peut ajouter une clause de réduction au

pragma parallel for On doit spécifier l’opération de réduction et la

variable sur laquelle s’applique la réduction OpenMP s’occupe de stocker les résultats

partiels dans des variables privées.

Page 28: Parallel Programming in C with MPI and OpenMP

Clause “Reduction” La clause réduction a la syntaxe

suivante:reduction (<op> :<variable>)

Opérateur Valeur initiale+ 0

- 0

* 1

& Tous les bits à 1

| Tous les bits à 0

^ Tous les bits à 0

&& 1

|| 0

max Maximum possible

min Minimum possible

Page 29: Parallel Programming in C with MPI and OpenMP

Exemple 1

double area, pi, x;int i, n;...area = 0.0;#pragma omp parallel for private(x) reduction(+:area)for (i = 0; i < n; i++) { x = (i + 0.5)/n; area += 4.0/(1.0 + x*x);}pi = area / n;

Page 30: Parallel Programming in C with MPI and OpenMP

Exemple 2#include <math.h> void reduction1(float *x, int *y, int n) { int i, b, c; float a, d; a = 0.0; b = 0; c = y[0]; d = x[0]; #pragma omp parallel for private(i) shared(x, y, n) \ reduction(+:a) reduction(^:b) \ reduction(min:c) reduction(max:d) for (i=0; i<n; i++) { a += x[i]; b ^= y[i]; if(c>y[i])c=y[i]; d = fmaxf(d,x[i]); }}

Page 31: Parallel Programming in C with MPI and OpenMP

Amélioration de la performance #1

Quelques fois, transformer une boucle for séquentielle en boucle for parallèle peut dégrader les performances

Le problème est que la transformation peut ajouter trop de “fork” et “join” par rapport au reste du calcul.

Quelques fois, inverser deux boucles inbriquées peut aider si: Le parallélisme est dans la boucle interne Après l’inversion, la boucle extérieure peut être

parallélisée L’inversion n’augmente pas trop les défauts de

caches.

Page 32: Parallel Programming in C with MPI and OpenMP

Exemplefor (i=1; i<m; i++) for (j=0; j<n; j++) a[i][j]= 2*a[i-1][j];

for (i=1; i<m; i++)#pragma omp parallel for for (j=0; j<n; j++) a[i][j]= 2*a[i-1][j];

#pragma omp parallel forfor (j=0; j<n; j++) for (j=1; j<m; i++) a[i][j]= 2*a[i-1][j];

Plusieurs fork/join

Plus de défauts de cache

Page 33: Parallel Programming in C with MPI and OpenMP

Amélioration de la performance #2

Lorsqu’une boucle a peu d’itérations, le temps supplémentaire des fork/join devient plus grand que le temps que l’on veut sauver par le parallélisme

La clause if indique au compilateur d’utiliser le parallélisme sous certaines conditions

#pragma omp parallel for if(n > 5000)

Page 34: Parallel Programming in C with MPI and OpenMP

Amélioration de la performance #3

Il est possible de choisir de quelle façon les itérations d’une boucle for seront affectées aux threads à l’aide de la clause schedule

On parlera d’ordonnancement des itérations Il y a deux principaux types

d’ordonnancement: Statique: L’ordonnancement est déterminé

avant l’exécution Dynamique: L’ordonnancement est faite en

cours d’exécution

Page 35: Parallel Programming in C with MPI and OpenMP

Ordonnancement statique ou dynamique

Ordonnancement statique Pas de charge de travail supplémentaire La charge de travail peut être mal

équilibrée Ordonnancement dynamique

Charge de travail supplémentaire Peut équilibrer la charge de travail

Page 36: Parallel Programming in C with MPI and OpenMP

Segments (chunks) Un segment est une suite d’itérations

contiguës Augmenter la taille des segments réduit la

charge supplémentaire de travail Décroitre la taille des segments permet de

mieux équilibrer la charge de travail entre les threads.

Page 37: Parallel Programming in C with MPI and OpenMP

Clause “schedule” Syntaxe:

schedule (<type>[,<segment> ])

Types permis: static: ordonnancement statique dynamic: ordonnancement dynamique guided: La taille des segments décroit

graduellement runtime: Le type est choisit à l’exécution en

fonction de la variable de l’environnement OMP_SCHEDULE

Page 38: Parallel Programming in C with MPI and OpenMP

Options schedule(static): La taille des segments est

environ n/t schedule(static,C): La taille des segments

est C schedule(dynamic): Une itération à la fois schedule(dynamic,C): C itérations à la fois

Page 39: Parallel Programming in C with MPI and OpenMP

Options (suite) schedule(guided, C):

Ordonnancement dynamique, la taille des segments diminue graduellement jusqu’à C

schedule(guided): C=1 schedule(runtime): Dépend de la

variable OMP_SCHEDULE; Exemple en Unix:setenv OMP_SCHEDULE “static,1”

ouexport OMP_SCHEDULE=“static,1”

Page 40: Parallel Programming in C with MPI and OpenMP

Autres formes de parallélisme

Jusqu’à maintenant, l’emphase a été mise sur la parallélisation des boucles for.

Parallélisme de données Nous allons voir d’autres situations favorables

au parallélisme de données:

Page 41: Parallel Programming in C with MPI and OpenMP

Traitement d’une liste de tâches

Page 42: Parallel Programming in C with MPI and OpenMP

Code séquentiel (1/2)int main (int argc, char *argv[]){ struct job_struct *job_ptr; struct task_struct *task_ptr; ... task_ptr = get_next_task (&job_ptr); while (task_ptr != NULL) { complete_task (task_ptr); task_ptr = get_next_task (&job_ptr); } ...}

Page 43: Parallel Programming in C with MPI and OpenMP

Code séquentiel (2/2)struct task_struct* get_next_task(

struct job_struct **job_ptr ) { struct task_struct *answer;

if (*job_ptr == NULL) answer = NULL; else { answer = (*job_ptr)->task; *job_ptr = (*job_ptr)->next; } return answer;}

Page 44: Parallel Programming in C with MPI and OpenMP

Stratégie de parallélisation

Chaque thread prend la prochaine tâche dans la liste et la complète. Cela est répété jusqu’à ce qu’il n’y ait plus de tâche.

On doit s’assurer que deux threads ne prennent pas la même tâche.

On doit donc définir une section critique.

Page 45: Parallel Programming in C with MPI and OpenMP

Le pragma “parallel” Précède un bloc de code devant être

exécuté par tous les threads.#pragma omp parallel

Note: Tous les threads exécutent le même code

Page 46: Parallel Programming in C with MPI and OpenMP

Code parallel (1/2)int main (int argc, char *argv[]){ struct job_struct *job_ptr; struct task_struct *task_ptr; ...#pragma omp parallel private(task_ptr) { task_ptr = get_next_task (&job_ptr); while (task_ptr != NULL) { complete_task (task_ptr); task_ptr = get_next_task (&job_ptr); } } ...}

Page 47: Parallel Programming in C with MPI and OpenMP

Code parallel (2/2)

char *get_next_task(struct job_struct **job_ptr) { struct task_struct *answer;#pragma omp critical { if (*job_ptr == NULL) answer = NULL; else { answer = (*job_ptr)->task; *job_ptr = (*job_ptr)->next; } } return answer;}

Page 48: Parallel Programming in C with MPI and OpenMP
Page 49: Parallel Programming in C with MPI and OpenMP

Le pragma “for” Le pragma “parallel” demande à tous les

threads d’exécuter tout le code dans le bloc.

Si le bloc contient une boucle for que l’on voudrait diviser entre les threads alors on peut utiliser le pragma “for”

#pragma omp for

Page 50: Parallel Programming in C with MPI and OpenMP

Exemplefor (i = 0; i < m; i++) { low = a[i]; high = b[i]; if (low > high) { printf ("Exiting (%d)\n", i); break; } for (j = low; j < high; j++) c[j] = (c[j] - a[i])/b[i];}

• La première boucle for ne peut pas être parallélisée• Paralléliser la seconde boucle est inefficace• Le pragma « parallel » seul est insuffisant

Page 51: Parallel Programming in C with MPI and OpenMP

Exemple#pragma omp parallel private(i,j){for (i = 0; i < m; i++) { low = a[i]; high = b[i]; if (low > high) { printf ("Exiting (%d)\n", i); break; }#pragma omp for for (j = low; j < high; j++) c[j] = (c[j] - a[i])/b[i];}

Page 52: Parallel Programming in C with MPI and OpenMP

Le pragma “single” Dans certaines situation on veut qu’un seul

thread exécute une certaine instruction Par exemple, un message en sortie

C’est le rôle du pragma “single”

Syntaxe:#pragma omp single

Page 53: Parallel Programming in C with MPI and OpenMP

Exemple#pragma omp parallel private(i,j,low,high)for (i = 0; i < m; i++) { low = a[i]; high = b[i]; if (low > high) {#pragma omp single printf ("Exiting (%d)\n", i); break; }#pragma omp for for (j = low; j < high; j++) c[j] = (c[j] - a[i])/b[i];}

Page 54: Parallel Programming in C with MPI and OpenMP

Clause “nowait” Le compilateur place une barrière de

synchronisation à la fin de chaque bloc couvert par un pragma de type single, parallel ou for.

La plupart du temps cela est nécessaire On peut enlever la barrière à l’aide de la

clause nowait

Page 55: Parallel Programming in C with MPI and OpenMP

Exemple#pragma omp parallel private(i,j, low, high){for (i = 0; i < m; i++) { low = a[i]; high = b[i];

#pragma omp single if (low > high) { printf ("Exiting (%d)\n", i); break; }#pragma omp for nowait for (j = low; j < high; j++) c[j] = (c[j] - a[i])/b[i];}

Page 56: Parallel Programming in C with MPI and OpenMP

Parallélisme de contrôle Nous n’avons vu jusqu’à maintenant que le

parallélisme de données OpenMP permet ausi d’affecter différentes

portions du code à différents threads

Page 57: Parallel Programming in C with MPI and OpenMP

Exemple v = alpha(); w = beta(); x = gamma(v, w); y = delta(); printf ("%6.2f\n", epsilon(x,y));

a lp h a b eta

g am m a d e lta

ep s ilo n

Page 58: Parallel Programming in C with MPI and OpenMP

Le pragma “parallel sections”

Précède un bloc contenant k blocs devant être exécutés en parallèle par k threads (1 thread par section)

Syntaxe:

#pragma omp parallel sections

Page 59: Parallel Programming in C with MPI and OpenMP

Le pragma “section” Précède chacun des blocs à l’intérieur d’un

bloc couvert par le pragma “parallel sections”

Peut être omis pour le premier bloc Syntaxe:

#pragma omp section

Page 60: Parallel Programming in C with MPI and OpenMP

Exemple#pragma omp parallel sections {#pragma omp section /* Optionnel */ v = alpha();#pragma omp section w = beta();#pragma omp section y = delta(); } x = gamma(v, w); printf ("%6.2f\n", epsilon(x,y));

Page 61: Parallel Programming in C with MPI and OpenMP

Autre approche

alp h a b e ta

g am m a d elta

ep s ilo n

Deux blocs:• alpha et beta• gamma et delta

Page 62: Parallel Programming in C with MPI and OpenMP

Le pragma “sections” Apparaît à l’intérieur d’un bloc de code

couvert par le pragma “parallel” Possède la même signification que le

pragma “parallel sections” Ajoute de la flexibilité à la façon

d ’organiser le parallélisme

Page 63: Parallel Programming in C with MPI and OpenMP

Exemple#pragma omp parallel { #pragma omp sections { v = alpha(); #pragma omp section w = beta(); } #pragma omp sections { x = gamma(v, w); #pragma omp section y = delta(); } } printf ("%6.2f\n", epsilon(x,y));

Page 64: Parallel Programming in C with MPI and OpenMP
Page 65: Parallel Programming in C with MPI and OpenMP

Quelques limitations avant OpenMP 3.0

2 exemples:

1.Parcours d’une liste chaînée2.Appels récursifs

Page 66: Parallel Programming in C with MPI and OpenMP

Parcours d’une liste chaînée

Une solution serait de transformer la liste en tableau: (perte de temps)

Page 67: Parallel Programming in C with MPI and OpenMP

Parcours d’une liste chaînée

Autre solution: (les threads boucles inutilement)

Page 68: Parallel Programming in C with MPI and OpenMP

Appels récursifs

• La création récursive des threads requiert trop de ressources• Comment obtenir une charge de travail équilibrée ?

Page 69: Parallel Programming in C with MPI and OpenMP

La directive « task »#pragma omp task bloc d’instructions

Une tâche est une instance d’une partie de code exécutable et de la mémoire associée.

Une tâche est générée lorsqu’un thread rencontre une directive task ou parallel

Page 70: Parallel Programming in C with MPI and OpenMP

La directive « task »#pragma omp task bloc d’instructions

Lorsqu’un thread rencontre une directive task il créé une tâche avec le bloc d’instructions suivant qu’il peut exécuter immédiatement ou reporter à plus tard.

Une tâche reportée peut être exécutée par n’importe quel thread du groupe actifs

Page 71: Parallel Programming in C with MPI and OpenMP

Exemple 1#pragma omp parallel {#pragma omp sections {#pragma omp section printf("1 ");#pragma omp section printf("2 ");#pragma omp section printf("3 ");#pragma omp section printf("4 "); }}

bash$ ./a.out3 1 2 4

Page 72: Parallel Programming in C with MPI and OpenMP

Exemple 2#pragma omp parallel {#pragma omp task printf("1 %d: ", omp_get_thread_num());#pragma omp task printf("2 %d: ", omp_get_thread_num());#pragma omp task printf("3 %d: ", omp_get_thread_num());#pragma omp task printf("4 %d: ", omp_get_thread_num()); }

bash$ ./a.out1 0: 2 0: 3 0: 4 0: 1 0: 2 0: 3 0: 4 6: 1 6: 2 6: 1 6: 1 6: 2 0: 3 6: 2 7: 3 7: 4 2: 4 6: 1 2: 1 2: 1 6: 4 7: 3 3: 2 2: 2 6: 3 0: 3 0: 4 2: 2 4: 4 5: 4 7: 3 6:

Page 73: Parallel Programming in C with MPI and OpenMP

Exemple 3#pragma omp parallel { #pragma omp single {#pragma omp task printf("1 %d: ", omp_get_thread_num());#pragma omp task printf("2 %d: ", omp_get_thread_num());#pragma omp task printf("3 %d: ", omp_get_thread_num());#pragma omp task printf("4 %d: ", omp_get_thread_num()); }}

bash$ ./a.out1 6: 2 7: 3 5: 4 1:

Page 74: Parallel Programming in C with MPI and OpenMP

Ex. Parcours d’une liste chaînée

Page 75: Parallel Programming in C with MPI and OpenMP

Ex. Parcours de plusieurs listes

Page 76: Parallel Programming in C with MPI and OpenMP

Ex. Fonction récursiveint fib(int n){ int a,b; if (n<2) return n;#pragma omp task shared(a) a=fib(n-1);#pragma omp task shared(b) b=fib(n-2);#pragma omp taskwait return a+b;}

Comment doit-on appeler fib la première fois?