Algorithmes Parallèles et Distribués
MPI 3. Aspects avancés de MPI
Camille Coti Franck Butelle César Rodríguez
LIPN, Université Paris 13 Formation Ingénieurs SupGalilée Info 3 et AIR 3
12/2020
Transparents préparés par Camille Coti, modifiés par César Rodríguez et Franck Butelle
(AIR3 + INFO3) APD – MPI 3 12/2020 1 / 24
Plan
1 Décomposition de domaine 3
2 Création de nouveaux communicateurs : Topologies cartésiennes 8
3 Création de nouveaux types de données 15
4 Opérations définies par l’utilisateur 22
(AIR3 + INFO3) APD – MPI 3 12/2020 2 / 24
Décomposition de domaine
Plan
1 Décomposition de domaine 3
Maître-esclave 4
Découpage en grille 6
Ghost Region 7
2 Création de nouveaux communicateurs : Topologies cartésiennes 8
3 Création de nouveaux types de données 15
4 Opérations définies par l’utilisateur 22
Décomposition de domaine Maître-esclave
Maître-esclave
Distribution des données
Le maître distribue le travail aux esclaves
Le maître démultiplexe les données, multiplexe les résultats Les esclaves necommuniquent pasentre eux
Efficacité
Files d’attentes de données et résultats au niveau du maître On retrouve la partie séquentielle de la loi d’Amdahl Communications : maître↔esclaves
Les esclaves ne travaillent que quand ils attendent des données ou qu’ils envoient leurs résultats
Seuls les esclaves participent effectivement au calcul
Possibilité d’un bon speedup à grande échellesi les communications sont rares Peu rentable pour quelques processus
Décomposition de domaine Maître-esclave
Schéma du Maître-esclave
Maître
Escl 1 Escl 2 Escl 3 Escl 4 Escl 5
Équilibrage de charge Statique :
Ï Utilisation deMPI_Scatterpour distribuer les données
Ï MPI_Gatherpour récupérer les résultats
Dynamique :
Ï Modepull: les esclaves demandent du travail
Ï Le maître envoie les «chunks» 1 par 1
(AIR3 + INFO3) APD – MPI 3 12/2020 5 / 24
Décomposition de domaine Découpage en grille
Découpage en grille
Grille de processus
On découpe les données et on attribue un processus à chaque sous-domaine
Décomposition 1D Découpage en bandes
0 1 2 3
Décomposition 2D
Découpage en rectangles(préférable si très grande taille)
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
(AIR3 + INFO3) APD – MPI 3 12/2020 6 / 24
Décomposition de domaine Ghost Region
Ghost region
Frontières entre les sous-domainesUn algorithme peut avoir besoin des valeurs des points voisins pour calculer la valeur d’un point
Traitement d’images (détection de contours...), automates cellulaires...
Réplication des données situées à la frontière
Chaque processus dispose d’un peu des données des processus voisins Mise à jour à la fin d’une étape de calcul
Création de nouveaux communicateurs : Topologies cartésiennes
Plan
1 Décomposition de domaine 3
2 Création de nouveaux communicateurs : Topologies cartésiennes 8
Création 9
Fonctions utiles 10
Exemples 11
sous-communicateurs 13
Communicateurs non cartésiens 14
3 Création de nouveaux types de données 15
4 Opérations définies par l’utilisateur 22
Création de nouveaux communicateurs : Topologies cartésiennes Création
Création d’une topologie cartésienne
Décomposition en structure géométrique
On transpose un communicateur sur une topologie cartésienne :
intMPI_Cart_create(MPI_Commcomm_old,intndims,const intdims[], const intperiods[],intreorder,MPI_Comm∗comm_cart)
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
En 2D
comm_old: le communicateur de départ ndims: ici 2
dims: nombre de processus dans chaque dimension (ici {4, 4}) periods: si on sort par un côté on revient par l’autre (1=vrai, 0=faux) reorder: autoriser ou non de modifier les rangs des processus
comm_cart: nouveau communicateur
(AIR3 + INFO3) APD – MPI 3 12/2020 9 / 24
Création de nouveaux communicateurs : Topologies cartésiennes Fonctions utiles
Fonctions utiles sur le nouveau communicateur
D’autres opérations sur les communicateurs avec topologie cartésienne :
intMPI_Cart_coords(MPI_Commcomm,intrang,intmaxdims,intcoords[]);
Permet d’obtenir lescoordonnéesd’un rang donné
intMPI_Cart_rank(MPI_Commcomm,intcoords[],int∗rang);
fonction inverse de la précédente :
Permet d’obtenir lerangdu processus associé aux coordonnées données Le rang est toujoursrelatifau communicateur !
intMPI_Cart_shift(MPI_Commcomm,intnumdim,intdeplact, int∗rang_src,int∗rang_dst);
Trouver les processusprécédant(rang_src) etsuivant(rang_dest) dans la dimension n°numdimavec un saut dedeplact(qui peut être<0 )
(AIR3 + INFO3) APD – MPI 3 12/2020 10 / 24
Création de nouveaux communicateurs : Topologies cartésiennes Exemples
Exemple (1/2)
intmain (intargc,char∗∗argv) { inttaille, rang, nrang, prev, succ;
intnbDims=1;
inttabDims[nbDims], newDims[nbDims];
intperiods[nbDims];
MPI_Commcomm;
MPI_Init(&argc, & argv);
MPI_Comm_size(MPI_COMM_WORLD, &taille);
MPI_Comm_rank(MPI_COMM_WORLD, &rang);
assert (taille >= 4);/∗ termine en cas d’échec du test ∗/
printf ("Je suis %d !\n", rang);
/∗ Une seule dimension, avec presque tous les threads ∗/
tabDims[0] = taille − 2;
periods[0] = 1;
MPI_Cart_create(MPI_COMM_WORLD, nbDims, tabDims, periods, 1, &comm);
Création de nouveaux communicateurs : Topologies cartésiennes Exemples
Exemple (2/2)
/∗ Certains threads n’appartiennent pas au nouveau communicateur ∗/
if(comm ==MPI_COMM_NULL) { printf ("Je suis %d, exclu !\n", rang);
}else{
/∗ Mon rang dans le nouv. communicateur ∗/
MPI_Comm_rank(comm, &nrang);
/∗ récupère mes coordonnées ∗/
MPI_Cart_coords(comm, nrang, nbDims, newDims);
printf ("%d: nouv. rang %d, x=%d\n", rang, nrang, newDims[0]);
MPI_Cart_shift(comm, 0, 1, &prev, &succ);
printf ("%d: nrang %d à dist 1: pre=%d succ=%d\n", rang, nrang, prev, succ);
MPI_Cart_shift(comm, 0, 2, &prev, &succ);
printf ("%d: nrang %d à dist 2: pre=%d succ=%d\n", rang, nrang, prev, succ);
MPI_Comm_free(&comm); /∗ pas sur ceux qui sont exclus ! ∗/
}
MPI_Finalize();
Création de nouveaux communicateurs : Topologies cartésiennes sous-communicateurs
Extraction de sous-communicateurs d’un comm. cartésien
intMPI_Cart_sub(MPI_Commcomm_old,
const intremain_dims[ ],MPI_Comm∗comm_new);
comm_old: le communicateur cartésien de départ
remain_dims: quelles dimensions sont dans le communicateur (1) ou non (0) comm_new: le nouveau communicateur contenant la sous grille dontfait partie le processus appelant.
X Y
Z
Exemple
Sicomm_olda une topologie (X,Y,Z) en 3×4×2 et remain_dimsest{1, 0, 1}, alors
MPI_Cart_sub() crée ? communicateurs avec une topologie en : ?
(AIR3 + INFO3) APD – MPI 3 12/2020 13 / 24
Création de nouveaux communicateurs : Topologies cartésiennes Communicateurs non cartésiens
Communicateurs non cartésiens
On peut créer des sous-communicateurs de communicateurs déjà créés MPI_Comm_create
MPI_Comm_split MPI_Graph_create MPI_Dist_graph_create . . .
(AIR3 + INFO3) APD – MPI 3 12/2020 14 / 24
Création de nouveaux types de données
Plan
1 Décomposition de domaine 3
2 Création de nouveaux communicateurs : Topologies cartésiennes 8
3 Création de nouveaux types de données 15
Schéma de fonctionnement 16
Bloc d’éléments contigus 17
Bloc d’éléments non contigus 18
Structures de données 20
4 Opérations définies par l’utilisateur 22
Création de nouveaux types de données Schéma de fonctionnement
Principe de fonctionnement
On définit le datatype (voir diapos suivantes)
Ï MPI_Type_contiguous,MPI_Type_vector,MPI_Type_indexed,MPI_Type_struct On l’enregistre(commit) "Obligatoire
Ï MPI_Type_commit(MPI_Datatype∗type) On peut récupérer sa taille en octets
Ï MPI_Type_size(MPI_Datatypetype,int∗ size) On le libère à la fin
Ï MPI_Type_free(MPI_Datatype∗type)
Extrait des types de base
MPI_CHAR MPI_SHORT MPI_INT
MPI_LONG MPI_UNSIGNED MPI_FLOAT MPI_DOUBLE MPI_LONG_DOUBLE MPI_BYTE MPI_UNSIGNED_CHAR MPI_UNSIGNED_SHORT MPI_UNSIGNED_LONG
Création de nouveaux types de données Bloc d’éléments contigus
Bloc d’éléments contigus
Création d’un block d’éléments :
int MPI_Type_contiguous(intcount,MPI_Datatypeoldtype, MPI_Datatype∗newtype );
NB :oldtypepeut être un type complexe.
oldtype
oldtype
oldtype
oldtype
VECTOR
(AIR3 + INFO3) APD – MPI 3 12/2020 17 / 24
Création de nouveaux types de données Bloc d’éléments non contigus
Bloc d’éléments non contigus
Vecteur de données
int MPI_Type_vector(intnbblocs,int longbloc , int pas,
MPI_Datatypeoldtype,MPI_Datatype∗newtype );
On lienbblocsblocs delongblocéléments séparés par un paspasd’éléments.
Exemple :nbblocs=3, longbloc=2, pas=5
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 ...
Quelles cases du tableaubuffsont envoyées ? intbuff[128];
MPI_Datatypedt;
intnbblocs=2, longbloc=2, pas=4;
MPI_Type_vector(nbblocs, longbloc, pas,MPI_INT, &dt);
MPI_Type_commit(&dt);
MPI_Send(buff, 2, dt, vois,tag,comm);//? ?
0 1 2 3 4 5 6 7 8 9 10 11 ...
(AIR3 + INFO3) APD – MPI 3 12/2020 18 / 24
Création de nouveaux types de données Bloc d’éléments non contigus
Recalculer la taille d’un bloc
Spécialement utile pour les blocs d’éléments non contigus
"Problème : Envoi de plusieurs blocs d’éléments non contigus
Les blocs d’éléments non contigus sont a priori inutilisables pour scatter, gather,...
Solution :
int MPI_Type_create_resized(MPI_Datatypeoldtype,MPI_Aintlb,MPI_Aintext, MPI_Datatype∗newtype)
Création d’un nouveau type identique à oldtype, sauf pour le calcul des offset : borne inf : lb(souvent 0) et borne sup :lb+ext(typiquement le premier sous élément d’un bloc d’éléments non contigus).
A la place du MPI_Send précédent
MPI_Type_create_resized(dt, 0, longbloc∗sizeof(int), &typeReduit);
MPI_Type_commit(&typeReduit);
MPI_Send(buff, 2, typeReduit, vois, tag, comm);
Création de nouveaux types de données Structures de données
Cas des struct
MPI n’a pas accès aux structures déclarées par le programmeur, il faut tout lui expliquer ! On donne les éléments, leur nombre et l’offset auquel ils sont positionnés :
intMPI_Type_create_struct(int count ,
int array_of_block_lengths[] ,
MPI_Aint array_of_displacements[] ,
MPI_Datatype array_of_types[] ,
MPI_Datatype *newtype );
count: nombre de champs dans la structure (taille des tableauxarray_of_...) array_of_block_lengths: nombre d’éléments dans le champ (pour les tableaux) array_of_displacements: permet à MPI de faire la (dé)sérialisation
array_of_types: idem, nécessaire pour la (dé)sérialisation newtype: le nom du nouveau type de données
Création de nouveaux types de données Structures de données
Création de type structuré — exemple
typedef struct{
inti; // bloc de 1 INT à l’adresse 0 doubled1;// bloc de 1 DOUBLE à l’adr. 8 doubled2;// bloc de 1 DOUBLE à l’adr. 16 charstr[5];// bloc de 5 CHAR à 2 l’adr. 24 } my_struct;
intlongblocs[4] = { 1, 1, 1, 5 };
MPI_Aintpositions[4] = {
offsetof (my_struct, i), // #include <stddef.h>
offsetof (my_struct, d1), offsetof (my_struct, d2), offsetof (my_struct, str) };
MPI_Datatypetypes[4] = {MPI_INT,MPI_DOUBLE,MPI_DOUBLE,MPI_CHAR};
MPI_Type_create_struct(4, longblocs, positions, types, &dt);
MPI_Type_commit(&dt);
(AIR3 + INFO3) APD – MPI 3 12/2020 21 / 24
Opérations définies par l’utilisateur
Plan
1 Décomposition de domaine 3
2 Création de nouveaux communicateurs : Topologies cartésiennes 8
3 Création de nouveaux types de données 15
4 Opérations définies par l’utilisateur 22
Création d’opération de réduction 23
Exemple 24
(AIR3 + INFO3) APD – MPI 3 12/2020 22 / 24
Opérations définies par l’utilisateur Création d’opération de réduction
Définition d’opérations de réduction
Syntaxe
Définition d’unenouvelle opérationà utiliser avec les opérations de réduction : intMPI_Op_create(MPI_User_function ∗function,intcommute,MPI_Op∗op);
La fonctionfunctiondoit avoir le prototype suivant :
voidMPI_User_function (void∗invec,void∗inoutvec,int∗len,MPI_Datatype∗dt);
La fonction doit être associative.
Elle peut être commutative (commute=1) ou pas
Opérations définies par l’utilisateur Exemple
Exemple d’implémentation de MPI_PROD avec des nombres complexes typedef struct{
doublereal,imag;
} Complex;
/∗ la fonction de l’utilisateur ∗/
voidmyProd(void∗inTab,void∗inoutTab,int∗len,MPI_Datatype∗dptr ) { Complex c; Complex ∗in = (Complex ∗) inTab;
Complex ∗inout = (Complex ∗) inoutTab;
for(inti=0; i< ∗len; ++i) {
c.real = inout−>real ∗ in−>real − inout−>imag∗in−>imag;
c.imag = inout−>real ∗ in−>imag + inout−>imag∗in−>real;
∗inout = c;
in++; inout++;
} }
intmain (intargc,char∗argv[]) { // ... declarations
MPI_Opmyop;
/∗ construction du type complexe pour MPI : 2 réels∗/
MPI_Type_contiguous(2,MPI_DOUBLE, &mpicomplex);
MPI_Type_commit(&mpicomplex);
/∗ creation de l’operateur ∗/
MPI_Op_create(myProd, 1, &myop);
//... initialisations
MPI_Reduce(sendBuf, recvBuf, 2, mpicomplex, myop, 0,MPI_COMM_WORLD);
//...
}