• Aucun résultat trouvé

Algorithmes Parallèles et Distribués Introduction à OpenMP Franck Butelle

N/A
N/A
Protected

Academic year: 2022

Partager "Algorithmes Parallèles et Distribués Introduction à OpenMP Franck Butelle"

Copied!
8
0
0

Texte intégral

(1)

Algorithmes Parallèles et Distribués

Introduction à OpenMP

Franck Butelle

LIPN, Université Paris 13

Formation Ingénieurs SupGalilée Info 3 et Info-Réseaux 3

11/2021

(INFO 3 + AIR 3) APD – OpenMP 2021 1 / 32

Plan

1

Introduction et modèle 3

2

Syntaxe 7

3

Notion de tâche, extensions de OpenMP 3.0 et plus 25

4

Fonctions utiles et conclusion 29

(INFO 3 + AIR 3) APD – OpenMP 2021 2 / 32

Introduction et modèle

Plan

1

Introduction et modèle 3

Introduction 4

Modèle et éléments pratiques 6

2

Syntaxe 7

3

Notion de tâche, extensions de OpenMP 3.0 et plus 25

4

Fonctions utiles et conclusion 29

Introduction et modèle Introduction

Pourquoi OpenMP ?

Améliorer la portabilité des programmes parallèles Faciliter leur développement

Standard industriel contrôlé par l’OpenMP Architecture Review Board : intérêts économiques

Spécifications détaillées :

www.openmp.org

(2)

Introduction et modèle Introduction

Qu’est-ce qu’OpenMP ?

Outil de programmation

multithread

via directives de compilation

Ï pragmas en C :#pragma omp

Ï bibliothèque de fonctions

Ï variables d’environnement

Compris par de nombreux compilateurs C ou Fortran

Ï ex. gcc (via libgomp) utiliser l’option-fopenmp:

F GCC 4.2 : version 2.5,

F GCC 4.4 : version 3.0,

F GCC 4.7 : version 3.1 (juillet 2011, 354 pages))

F GCC 4.9 : version 4.0 d’OpenMP (juillet 2014/juillet 2013, 320 pages)

F GCC 6.1 : version 4.5 d’OpenMP (nov. 2015, 282 pages sans les exemples)

Dans cette introduction on ne présentera OpenMP que via le langage C

Ce cours se base sur le tutoriel dewww.openmp.orget les spécifications de OpenMP 3.1

(INFO 3 + AIR 3) APD – OpenMP 2021 5 / 32

Introduction et modèle Modèle et éléments pratiques

Modèle et éléments pratiques

Programmation parallèle (non distribuée) à mémoire partagée (SPMD) Parallélisme de type

fork/join

Ï Succession de régions séquentielles et de régions parallèles séq.

fork join

séq. séq.

Ï Un processusmaître (de rang 0) créée des threads (suite aux directives d’OpenMP) pour les sections parallèles

Ï introduction de barrières de synchro (join) après les sections parallèles.

Un thread peut récupérer son numéro par

omp_get_thread_num()

. . .et le nbre de threads par

omp_get_num_threads()"

(INFO 3 + AIR 3) APD – OpenMP 2021 6 / 32

Syntaxe

Plan

1

Introduction et modèle 3

2

Syntaxe 7

Règles générales 8

Parallélisme dans un bloc 10

Parallélisme par section 12

Partage du travail : la boucle for 14

Combinaison de directives avec for 15

Directives single,master,critical 21

Directives barrier, atomic et threadprivate 22

3

Notion de tâche, extensions de OpenMP 3.0 et plus 25

4

Fonctions utiles et conclusion 29

Syntaxe Règles générales

Règles générales de syntaxe

Sensible à la casse (Maj

6=

Min)

Directives OpenMP doivent suivre conventions standards des directives de compilation du C/C++ (#pragma)

Syntaxe :

#pragma omp directive

Ï Uniquement un nom de directive OpenMP par pragma sauf éventuellement#pragma ompparalleldirective

Chaque directive OpenMP s’applique à au moins la ligne suivante ou au bloc structuré qui suit (

{}

).

Les directives OpenMP trop longues peuvent être prolongées sur la

ligne suivante grâce à

\

(3)

Syntaxe Règles générales

Fixer le nombre de threads

Par variable d’environnement, exemple en BASH :

export OMP_NUM_THREADS=5

ou dans le code par :

omp_set_num_threads(5)

ou par directive :

#pragma omp parallel num_threads(5)

Sinon OpenMP calcule automatiquement ce qu’il peut faire de mieux (en fonction du nbre de cœurs).

(INFO 3 + AIR 3) APD – OpenMP 2021 9 / 32

Syntaxe Parallélisme dans un bloc

Exemple 1 : OpenMP vs Posix threads

void∗travail(void∗param) {... }/∗ au plus 1 unique param... pensez à struct ! ∗/

pthread_t tid[4];

for(i = 1; i < 4; i++) {/∗ Seulement 3 threads de créés ∗/

pthread_create (&tid[i], NULL, travail, params);

travail(params);/∗ le thread 0 fait le même travail ∗/

for(i = 1; i < 4; i++)/∗ Barrière de synchro ∗/

pthread_join (tid[i]);

/∗ Code séquentiel avant...

ATTENTION:passage à la ligne obligatoire à la fin des #pragma ∗/

#pragma ompparallelnum_threads(4) { /∗ début de section multithread ∗/

travail(param1,param2,...);

} /∗ ici barrière de synchro implicite ∗/

/∗ Code séquentiel après... ∗/

(INFO 3 + AIR 3) APD – OpenMP 2021 10 / 32

Syntaxe Parallélisme dans un bloc

Syntaxe de #pragma omp

#pragma ompdirective[clause...]

bloc structuré

directive

:

parallel,sections, single, barrier, atomic, for, . . .(ou combinaison) Uneclause(classique) :

if(condition) : si la condition est vraie (6=0) alors la section est exécutée en parallèle sinon en séquentiel

private(list) : liste de variables privées à chaque thread shared(list) : liste de variables partagées entre tous les threads

firstprivate(list) : variables privées mais initialisées à leur valeur d’avant la section parallèle

lastprivate(list) : la valeur finale des variables privées est copiée vers la variable initiale en fin d’exécution de la section

default(shared | none) : attribution par défaut des variables (none : aucune valeur par défaut)

reduction(operator : list) : une copie privée est faite pour chaque variable de la listelistet en fin d’exécution une réduction par l’opérateuroperatorest opérée sur les valeurs (exemple :+, voir plus loin).

Syntaxe Parallélisme par section

Exemple parallélisme par section

Une section ne sera parcourue que par un et un seul thread.

/∗ Code séquentiel avant... ∗/

#pragma ompparallelshared(a,b,c,d)private(i) { #pragma ompsectionsnowait

{ /∗ nowait : pas de synchro auto en fin ∗/

#pragma ompsection for(i = 0 ; i < N ; i++)

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

#pragma ompsection for(i=0 ; i < N ; i++)

d[i] = a[i] ∗ b[i];

} /∗ fin des sections ∗/

} /∗ fin de section parallèle ∗/

/∗ Code séquentiel après... ∗/

(4)

Syntaxe Parallélisme par section

Qu’affiche le code suivant ?

#include <stdio.h>

#include <omp.h>

#define NT 4 intmain() {

intc = 0;

omp_set_dynamic(0);// Sinon l’instruction suivante ne sert à rien omp_set_num_threads(NT);

#pragma ompparallel

#pragma ompsectionsfirstprivate( c ) {#pragma ompsection

{ c++ ; printf("Thr%d: c=%d\n",omp_get_thread_num(),c);

}#pragma ompsection

{ c++ ; printf("Thr%d: c=%d\n",omp_get_thread_num(),c);

}} return0;

}

(INFO 3 + AIR 3) APD – OpenMP 2021 13 / 32

Syntaxe Partage du travail : la boucle for

Exemple de partage du travail par for

#include <omp.h>

#define CHUNKSIZE 100

#define N 1000 main () {

inti, chunk;

doublea[N], b[N], c[N];

/∗ Quelques initialisations ∗/

for(i=0 ; i < N ; i++) a[i] = b[i] = i ∗ 1.0;

chunk = CHUNKSIZE;

/∗ On peut écrire les 2 pragmas suivantes en une seule ∗/

#pragma ompparallelshared(a,b,c,chunk)private(i) {#pragma ompforschedule(dynamic,chunk)nowait

for(i=0 ; i < N ; i++) c[i] = a[i] + b[i];

}/∗ fin de section parallèle ∗/

}

(INFO 3 + AIR 3) APD – OpenMP 2021 14 / 32

Syntaxe Combinaison de directives avec for

Exemple de combinaison de directives

#include <omp.h>

#define N 1000

#define CHUNKSIZE 100 intmain () {

inti, chunk;

floata[N], b[N], c[N];

/∗ Initialisation du tableau ∗/

for(i=0; i < N; i++) a[i] = b[i] = i ∗ 1.0;

chunk = CHUNKSIZE;

/∗ Le thread 0 fait la boucle pour i de 0 à chunk−1 puis la prochaine tranche libre

∗ le thread 1 fait la boucle pour i de chunk à 2∗chunk −1 (idem)...∗/

#pragma ompparallelforshared(a,b,c,chunk)private(i)schedule(static,chunk) for(i = 0 ; i < N ; i++)

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

/∗ synchro automatique en fin de bloc ∗/

}

Syntaxe Combinaison de directives avec for

Syntaxe du partage du travail par for

#pragma ompfor[clause...] boucle_for

clause:

schedule(type[,chunk]) : décrit comment les itérations de la boucle sont réparties parmi les threads :

Ï static: assignation statique aux threads en morceaux de taille chunk. Si chunk n’est pas précisé, le travail est équitablement réparti.

Ï dynamic: commestaticsauf que lorsqu’un thread a fini un chunk, il est dynamiquement assigné à un autre. La valeur par défaut de chunk est 1.

Ï guided: commedynamicsauf que la taille des blocs diminue à chaque fois qu’une partie du travail est attribuée à un thread. Taille initiale d’un bloc : nbre_iter/nbre_threads, ensuite

nbre_iter_restant/nbre_threads. chunk donne la taille min. d’un bloc.

Ï runtime: décision retardée à l’exécution (lit var. OMP_SCHEDULE)

Ï auto: (par défaut) la décision est prise par le compilateur.

ordered: les exécutions doivent être ordonnées comme dans un pgm séquentiel

(5)

Syntaxe Combinaison de directives avec for

Limitations de la directive for

"forme canonique obligatoire

for(init−expr ; test−expr ; incr−expr) bloc−structuré

init-expr : une des syntaxes suivantes (exclusivement) :

Ï var= lb

Ï integer-type var= lb

Ï pointer-typevar= lb

Ï lb, b :doivent être d’un type compatible avecvaret doivent être invariants

test-expr : (idem)

Ï varoperateur-relationnelb

Ï boperateur-relationnelvar

Ï operateur-relationnel: <, <=, >, >=, var

: (idem)

Ï type int signée ou non.

Ï type pointeur

Ï Cette variable sera auto. déclarée privée dans la boucle, doit être modif. que par incr-expr. Si non spécifiéelastprivate, sa valeur est ? en sortie.

(INFO 3 + AIR 3) APD – OpenMP 2021 17 / 32

Syntaxe Combinaison de directives avec for

Limitations de la directive for (suite)

forme canonique obligatoire

#pragma ompparallelfor

for(init−expr ; test−expr ; incr−expr) bloc−structuré

incr-expr : (idem)

Ï ++varouvar++ou--varouvar--

Ï var+= incrouvar-= incr

Ï var=var+ incrouvar= incr +varouvar=var- incr

Ï incr :une expression d’un type entier, invariant pendant l’exécution de la boucle.

bloc-structuré :

pas de break ni de return.

(INFO 3 + AIR 3) APD – OpenMP 2021 18 / 32

Syntaxe Combinaison de directives avec for

Exemple de boucle for illégale

Recherche si une valeur est présente dans un tableau

#pragma ompparallelfor for(i=0; i< 10000; i++)

if(t[i]==v) return(1);

return(0); ILLEGAL

Peut être écrit :

trouve=0;

/∗ On prend le risque d’avoir plusieurs threads qui écrivent 1 dans trouve ∗/

#pragma ompparallelfor for(i=0; i< 10000; i++)

if(t[i]==v) trouve=1;

return(trouve==1);

Syntaxe Combinaison de directives avec for

Exemple de réduction

r = 0;

/∗ reduction => chaque thread a une copie privée de r le temps du calcul∗/

#pragma ompparallelforreduction(+:r)private(t) for(i = 0 ; i < 1000 ; i++){

t = foo(i);

r = r+t

/∗ synchro automatique en sortie avec r final = somme des r} j pourj de 0 à num_threads1∗/

Contraintes d’utilisation de la réduction :

Les variables de la liste doivent être partagées dans la région.

Les variables ne peuvent pas être des tableaux ou des struct Initialisation par l’élément neutre

Traitement sur la copie locale Réduction à la sortie

Opérateurs d’aggrégation connus :+, *, -, /, &, |, &&, ||,^,max et min (à partir de OpenMP 3.1)

(6)

Syntaxe Directives single,master,critical

Directive single,master,critical

#pragma ompsingle[clause ...]

bloc structuré

#pragma ompmaster bloc structuré

#pragma ompcritical[ name ] bloc structuré

single: Un seul thread exécute la section, les autres attendent à la fin du bloc.

master: Seul le maître (rang 0) exécute la section, les autres threads ignorent cette section. Attention, il n’y a pas de barrière de synchro à la fin.

critical: C’est une section critique donc exécutée que par un seul thread à la fois. Lenameoptionnel permet d’identifier les sections critiques de sorte que plusieurs sections portant le même nom seront globalement critiques.

Toutes les sections qui n’ont pas de nom sont considérées comme portant le même.

(INFO 3 + AIR 3) APD – OpenMP 2021 21 / 32

Syntaxe Directives barrier, atomic et threadprivate

Directives barrier, atomic et threadprivate

#pragma ompbarrier lignes de code

#pragma ompatomic une_seule_ligne

#pragma ompthreadprivate(list)

barrier

: barrière de synchro

atomic

: sorte de mini critical

threadprivate

: permet de définir une liste de variables privées

réutilisable dans une future autre section parallèle. La première section parallèle les utilisant doit les initialiser.

Ï Directives pour l’initialisation des variables threadprivate :

F copyin: copie des variables du thread maître vers les autres

F copyprivate: diffusion des variables threadprivate précisées du thread courant vers les autres"

(INFO 3 + AIR 3) APD – OpenMP 2021 22 / 32

Syntaxe Directives barrier, atomic et threadprivate

Exemple 1 d’utilisation de threadprivate

#include <omp.h>

inta, b, i, tid;

floatx;

#pragma ompthreadprivate(a, x) intmain () {

/∗ Interdit l’utilisation des threads dynamiques ∗/

omp_set_dynamic(0);

printf("1ere Section parallèle:\n");

#pragma ompparallelprivate(b,tid) { tid =omp_get_thread_num();

a = tid ; b = tid ; x = 1.1 ∗ tid +1.0;

printf("Thread %d: a,b,x= %d %d %f\n",tid,a,b,x);

}/∗ Fin de la 1ere section parallèle ∗/

printf("Le thread maître fait un travail séq. ici\n");

printf("2eme Section parallèle\n");

#pragma ompparallelprivate(tid) { tid =omp_get_thread_num();

printf("Thread %d: a,b,x= %d %d %f\n",tid,a,b,x);

}/∗ fin de section parallèle ∗/

}

Syntaxe Directives barrier, atomic et threadprivate

Exemple 2 d’utilisation de threadprivate

#include <stdlib.h>

float∗ work;

intsize;

floattol;

#pragma ompthreadprivate(work,size,tol) voidbuild() {

inti;

work = malloc(sizeof(float)∗size );

for(i = 0 ; i < size ; ++i) work[i] = tol;

}voidcopyin_example(floatt,intn ) {

/∗ initialisation des variables threadprivate en séq.

donc pour le maître uniquement∗/

tol = t;

size = n;

/∗ on lance build en parallele mais avant on recopie les var.∗/

#pragma ompparallel copyin(tol,size) build();

}

(7)

Notion de tâche, extensions de OpenMP 3.0 et plus

Plan

1

Introduction et modèle 3

2

Syntaxe 7

3

Notion de tâche, extensions de OpenMP 3.0 et plus 25

Pourquoi une gestion par tâche 26

Directive task 27

4

Fonctions utiles et conclusion 29

(INFO 3 + AIR 3) APD – OpenMP 2021 25 / 32

Notion de tâche, extensions de OpenMP 3.0 et plus Pourquoi une gestion par tâche

Pourquoi une gestion par tâche

Le parcours de liste chaînée ou d’arbre est très inefficace par

sections

ou par

single.

En fait tout problème irrégulier pose problème :

Ï boucles non bornées

Ï programmes récursifs

Ï schémas producteur/consommateur

Ï ...

Liste de choses à faire : liste de tâches, un thread dispo effectue la première tâche de la liste etc.

Gestion de tâches : à partir de OpenMP 3.0

La directive

parallel, à partir de OpenMP 3.0, crée des tâches

Ï Une tâche implicite est créée par thread

Un thread peut suspendre (suspend) une tâche ou la lancer/reprendre (start/resume)

(INFO 3 + AIR 3) APD – OpenMP 2021 26 / 32

Notion de tâche, extensions de OpenMP 3.0 et plus Directive task

#pragma omptask[clause[[,] clause] ...]

bloc_structuré

Un thread qui rencontre la directive task crée une tâche : Un ens. d’un code et de variables.

Des tâches peuvent créer d’autres tâches et être utilisées avec les autres directives.

Quelquesclause en plus des classiques :

Si pas de clause alors var. globales partagées et pour le restefirstprivate if(condition) : si la condition est fausse la tâche actuelle est suspendue et la nouvelle est lancée immédiatement. Quand elle finie, la parente peut continuer : permet éventuellement des optimisations.

untied: par défaut une tâche est associée/attachée à un thread («tied») avec mécanismes anti deadlocks etc. : peut introduire des lenteurs... On peut essayer de détacher par untied (").

mergeable: la création de cette nouvelle tâche peut être, suivant l’implémentation, simplement fusionnée avec la tâche courante.

Notion de tâche, extensions de OpenMP 3.0 et plus Directive task

Exemple d’utilisation de task

typedef structElement { intval;

structElement ∗ next;

} Element;

typedefElement ∗ Liste;

voidparcours_liste(Liste l) { Element e ;

for(e = l ; e != NULL ; e= e− >next)

#pragma omptask travail (e) ;

/∗attendre fin des tâches filles∗/

#pragma omptaskwait }

intmain () { Liste l;

/∗ Ici création de la liste ∗/

...

#pragma ompparallel

{/∗Un seul thread va créer toutes les tâches qui seront exécutées en parallèle∗/

#pragma ompsingle parcours_liste(l) ; }...

}

(8)

Fonctions utiles et conclusion

Plan

1

Introduction et modèle 3

2

Syntaxe 7

3

Notion de tâche, extensions de OpenMP 3.0 et plus 25

4

Fonctions utiles et conclusion 29

Attention à la mémoire en C ! 30

Quelques fonctions OpenMP utiles 31

Conclusions sur OpenMP 32

(INFO 3 + AIR 3) APD – OpenMP 2021 29 / 32

Fonctions utiles et conclusion Attention à la mémoire en C !

Mode d’allocation Zone

mémoire Commentaire

Statique

Données

(.BSS ou .Data)

En C mot clé static ou var. globale – préalloué à la compilation – Efficace en temps mais gaspillage possible

Dynamique

Pile (stack)

Allocation automatique, Taille limitée à≈ 4 Mio. (idem)

Tas (heap)

Allocation à la demande du program- meur : malloc. Doit être libérée à la main : free – Pas de gaspillage mém.

Exemples

chars[TAILLE1];/∗ Variable globale: TAILLE1 max 128 Tio ∗/

intmafonc (inti) {

chart[TAILLE2];/∗ alloc auto: TAILLE2 max 4 Mio ! ∗/

char∗u=malloc(sizeof(char)∗TAILLE3);/∗ TAILLE3 max 128Tio ∗/

...

(INFO 3 + AIR 3) APD – OpenMP 2021 30 / 32

Fonctions utiles et conclusion Quelques fonctions OpenMP utiles

Quelques fonctions OpenMP utiles

voidomp_set_num_threads(int num_threads) : le nbre de threads qui va être utilisé dans la prochaine section parallèle. Est prioritaire sur la var. d’env.

OMP_NUM_THREADS

intomp_get_num_threads(void) : retourne le nbre de threads de la section parallèle en cours.

intomp_get_max_threads(void) : retourne la valeur max de threads ou celle fixée par omp_set_num_threads

intomp_get_thread_num(void) : retourne le numéro de thread qui l’appelle, 0 pour le maître.

intomp_get_thread_limit(void) : retourne le nbre max de threads dispo pour un programme (à partir d’OpenMP 3.0)

intomp_get_num_procs(void) : retourne le nbre de processeurs/cœurs disponibles

intomp_in_parallel(void) : retourne 0 si dans une section séquentielle.

voidomp_set_dynamic(int dynamic_threads) : active la gestion dynamique du nbre de threads si dynamic_threads=1. Doit être lancée dans une section séquentielle et est prioritaire sur la var. d’env. OMP_DYNAMIC.

intomp_get_dynamic(void) : retourne 0 si la gestion dynamique est désactivée.

doubleomp_get_wtime(void): retourne la date en secondes.

Fonctions utiles et conclusion Conclusions sur OpenMP

Conclusions sur OpenMP

Outil facile d’accès dans un premier temps mais attention aux variables privées/partagées

Très bonne portabilité Très bon sur les boucles for

On peut encore gagner du temps de calcul avec l’utilisation directe des threads.

Pour aller plus loin :

http://www.openmp.org/specifications

pour les détails des

spécifications version par version et des résumés.

Références

Documents relatifs

This paper proposes a communication-aware task re-ordering strategy for OpenMP that aims at reducing idle periods in hybrid MPI+OpenMP (tasks) applications by favoring tasks on a

Parallélisme au niveau matériel Processeur super-scalaire.

1962 premier ordinateur multiprocs (4) mais système non parallèle 1964 premier ordi double cœur et premier ordinateur à structure parallèle.. 1965 premier

Algorithmes distribués classiques (1ère partie) Éveil distribué par inondation.

Tout mesg envoyé à un site est placé dans sa file d’attente.. L’ordonnanceur du réseau permet aux

Si on a un algo d’élection alors on a un algo d’AC (en ajoutant 2m messages et D unités de temps). AC −→ élection (en ajoutant n − 1 messages et h unités de temps, où h est

pire des cas atteint pour un anneau avec identités en ordre décroissant et tous initiateurs... Donc 3n −

Ï Rang dans MPI_COMM_WORLD = rang absolu dans l’application Utilisé pour les envois / réception de messages. La norme MPI Structure d’un