1 / 62

Parallel Programming in C with MPI and OpenMP

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

elsu
Download Presentation

Parallel Programming in C with MPI and OpenMP

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Parallel Programmingin C with MPI and OpenMP Chapitre 17 Open MP Michael J. Quinn

  2. 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++

  3. Modèle à mémoire partagée Processeur Processeur Processeur Processeur Mémoire Les processeurs interagissent et se synchronisent à l’aide de variables partagées.

  4. 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.

  5. Parallélisme avec Fork et Join

  6. 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.

  7. 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.

  8. 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>

  9. 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

  10. Forme canonique d’une boucleparallel for

  11. 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.

  12. Variables privées et partagées … }

  13. Comment le système sait-il combien de threads il faut créer? Variable de l’environnement: OMP_NUM_THREADS 4 fonctions utiles: • omp_get_num_procs • omp_set_num_threads • omp_get_num_threads • omp_get_thread_num

  14. 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)

  15. 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)

  16. Fonction omp_get_num_threads • Retourne le nombre de threads actifs. int omp_get_num_threads (void)

  17. Fonction omp_get_thread_num • Retourne le numérodu thread. int omp_get_thread_num(void)

  18. 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

  19. Clause “private” • Clause: Composante optionnelle à un pragma • Clause “Private”: indique au compilateur de créer une ou plusieurs variables privées. private ( <variable list> )

  20. 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]);

  21. 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 • La modification d’une valeur est effective aussi pour les autres itérations.

  22. Sections critiques Exemple: Approximation de 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;

  23. 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;

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

  25. Pragma “critical” • Section critique: portion de code qui ne peut être exécuté que par un seul thread à la fois. • On met#pragma omp criticaldevant le bloc de code C.

  26. Exemple 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; #pragma omp critical area += 4.0/(1.0 + x*x); } pi = area / n; Correct mais inefficace!

  27. 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.

  28. Clause “Reduction” • La clause réduction a la syntaxe suivante:reduction (<op> :<variable>)

  29. 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;

  30. 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]); } }

  31. 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.

  32. Exemple for (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 for for (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

  33. 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)

  34. 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

  35. 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

  36. 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.

  37. 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

  38. 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

  39. 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” ou export OMP_SCHEDULE=“static,1”

  40. 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:

  41. Traitement d’une liste de tâches

  42. 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); } ... }

  43. 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; }

  44. 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.

  45. 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

  46. 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); } } ... }

  47. 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; }

  48. 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

  49. Exemple for (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

More Related