Télécharger la présentation
La présentation est en train de télécharger. S'il vous plaît, attendez
1
Programmation parallèle
MPI & OpenMP Présentée par UCI ADMIN
2
programmation parallèle
l'exécution d'un programme en plusieurs processus L’intérêt de faire de la programmation parallèle Réduire le temps de restitution ; Effectuer de plus gros calculs ; Exploiter le parallélisme des processeurs modernes (multi-cœurs, multithreading). Methode
3
MPI vs OpenMP OpenMP utilise un schéma à mémoire partagée
MPI la mémoire est distribuée.
4
Modèle de programmation par échange de messages
5
Modèle de programmation par échange de messages
le programme est écrit dans un langage classique (Fortran, C, C++, ...) ; Toutes les variables du programme sont privées et résident dans la mémoire locale allouée à chaque processus ; Une donnée est échangée entre deux ou plusieurs processus via un appel dans le programme
6
Concepts de l’échange de messages
Un message est constitué de paquets de données transitant du processus émetteur au(x) processus récepteur(s) En plus des données à transmettre, un message doit contenir aussi: l’identificateur du processus émetteur ; le type de la donnée ; La longueur ; l’identificateur du processus récepteur.
7
Concepts de l’échange de messages
8
Environnement MPI Openmpi Supporte tous type de réseau Mpich2 Ne supporte pas l’InfiniBand Mvapitch MPI over InfiniBand dérivé de mpich supporte l’InfiniBand
9
l’environnement MPI Inclure le fichier mpi.h MPI_INIT() permet d’initialiser l’environnement MPI MPI_FINALIZE() désactive cet environnement En C/C++: int MPI_Init(int *argc, char ***argv); int MPI_Finalize(void);
10
Communicateurs Les opérations effectuées par MPI portent sur des communicateurs MPI_COMM_WORLD Le communicateur par défaut Il comprend tous les processus actifs.
11
Rang et nombre de processus
MPI_Comm_size() connaitre le nombre de processus MPI_Comm_size(MPI_COMM_WORLD , alltasks , code); MPI_Comm_rank retourne le rang d’un processus MPI_Comm_rank(MPI_COMM_WORLD , rang , code) 0<=rang<MPI_COMM_WORLD
12
Rang et nombre de processus
Exemple: Ecrire un script ou chaque processus affiche Je suis le processus rang parmi alltasks Changer le script pour que chaque processus affiche aussi le nom de la machine dont il s’exécute.
13
Rang et nombre de processus
Solution: #include <stdio.h> #include <mpi.h> int main (int argc, char* argv[]) { int nbProc, myRank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); printf("Je suis le processus %d parmi %d \n", myRank , nbProc); MPI_Finalize(); }
14
Rang et nombre de processus
Solution avec nom de la machine : char processor_name[20]; int name_len; MPI_Get_processor_name(processor_name, &name_len); printf("Je suis le processus %d parmi %d machine %s\n", myRank , nbProc, processor_name);
15
Rang et nombre de processus
Exécution : mpicc -o result hello.c mpirun –np 4 result Je suis le processus 0 parmi 4 machine haytham0 Je suis le processus 1 parmi 4 machine haytham0 Je suis le processus 2 parmi 4 machine haytham0 Je suis le processus 3 parmi 4 machine haytham0
16
Communications point à point
17
Communications point à point
Passe entre deux processus émetteur et récepteur Routines basiques de communication Opération d’envoi MPI_Send() MPI_Send(message,longueur,type,rang_dest,etiquette,comm ,code) Envoi, à partir de l’adresse message, d’un message de taille longueur, de type type, étiqueté etiquette, au processus rang_dest dans le communicateur comm Cette opération est bloquante : l’exécution reste bloquée jusqu’à ce que le contenu de message puisse être réécrit sans risque d’écraser la valeur qui devait être envoyée.
18
Communications point à point
Opération de réception MPI_Recv() MPI_Recv(message,longueur,type,rang_source,etiquette,comm,statut,code) Réception, à partir de l’adresse message, d’un message de taille longueur, de type type, étiqueté etiquette, du processus rang_source. Cette opération est bloquante : l’exécution reste bloquée jusqu’à ce que le contenu de message corresponde au message reçu
19
Communications point à point
Types de données de base C MPI_CHAR signed char MPI_SHORT signed short MPI_INT signed int MPI_LONG signed long int MPI_UNSIGNED_CHAR unsigned char MPI_UNSIGNED_SHORT unsigned short MPI_UNSIGNED unsigned int MPI_UNSIGNED_LONG unsigned long int MPI_FLOAT float MPI_DOUBLE double MPI_LONG_DOUBLE long double
20
Communications point à point
One sided point to point Exercice : Copier le script précédent dans un fichier nommé one_sided.c Modifier le de sorte que le processus master envoie au processus worker un message d’introduction contenant son rang Le worker reçoit le message et l’affiche Indication: Utiliser la fonction sprintf pour générer le buffer du message
21
Communications point à point
Solution : #include <stdio.h> #include <mpi.h> int main (int argc, char* argv[]) { //déclaration de variables int nbProc, myRank; int tag,source,destination,count; char buffer[30], msg[20]="hello my Rank is"; char rbuf[30]; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); // initialisation tag=10; source=0; destination=1; count=30;
22
Communications point à point
Suite : if (myRank == source ){ //code executé par processus master 0 sprintf(buffer,"%s %d",msg,myRank ); MPI_Send(&buffer,count,MPI_CHAR,destination,tag,MPI_COMM_WORLD); } if (myRank == destination){ //code executé par processus worker 1 MPI_Recv(&buffer,count,MPI_CHAR,source,tag,MPI_COMM_WORLD,&status); printf("processor %d got %s\n",myRank,buffer ); MPI_Finalize();
23
Rang et nombre de processus
Exécution : mpicc -o result one_sided.c mpirun –np 4 result processor 1 got hello my Rank is 0
24
Communications point à point
Two sided point to point Exercice : Modifier le script one_sided.c à un autre nommé two_sided.c , ou le worker affiche le message du master et répond par son message d’introduction
25
Communications point à point
Solution: #include <stdio.h> #include <mpi.h> int main (int argc, char* argv[]) { int nbProc, myRank; int tag,source,destination,count; char sbuf[40], rbuf[40], smsg[20]="hello my Rank is", rmsg[30]="Well Received From Processor"; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); tag=10; source=0; destination=1; count=40;
26
Communications point à point
Suite: if (myRank == source ){ //préparer le buffer d’envoie sprintf(sbuf,"%s %d",smsg,myRank ); //envoyer le message au worker 1 MPI_Send(&sbuf,count,MPI_CHAR,destination,tag,MPI_COMM_WORLD); //recevoir la réponse depuis worker 1 MPI_Recv(&rbuf,count,MPI_CHAR,destination,tag,MPI_COMM_WORLD,&status); printf(" processor %d got %s\n ", myRank,rbuf ); }
27
Communications point à point
Suite: if (myRank == destination){ //recevoir le message du master MPI_Recv(&sbuf,count,MPI_CHAR,source,tag,MPI_COMM_WORLD,&status); printf("processor %d got %s\n",myRank,sbuf); //preparer le buffer de la réponse sprintf(rbuf,"%s %d",rmsg,myRank); //envoyer la réponse MPI_Send(&rbuf,count,MPI_CHAR,source,tag,MPI_COMM_WORLD); } MPI_Finalize();
28
Rang et nombre de processus
Exécution : mpicc -o result hello.c mpirun –np 4 result processor 0 got Well Received From Processor 1 processor 1 got hello my Rank is 0
29
Communications point à point
One to all point to point Exercice : Modifier le script one_sided.c à un autre nommé one_to_all.c pour que tous les workers reçoivent et affichent le message du master
30
Communications point à point
Solution : int main (int argc, char *argv[]) { int alltasks, taskid, i, length=20 ; char sbuf[20], rbuf[20] ; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &taskid); MPI_Comm_size(MPI_COMM_WORLD, &alltasks); if (taskid == 0){ sprintf(sbuf, "Hello my Rank is %d", taskid ); for (i=1 ; i< alltasks ; i++) { //le master envoie un message à tous les workers MPI_Send(&sbuf, length, MPI_CHAR, i, 0, MPI_COMM_WORLD) ; }}
31
Communications point à point
Suite : else { MPI_Recv(&rbuf, length, MPI_CHAR, 0, 0, MPI_COMM_WORLD,MPI_STATUS_IGNORE); printf("processor %d got %s\n",taskid, buf); } MPI_Finalize(); Exécution : mpirun -np 4 result processor 2 got Hello my Rank is 0 processor 3 got Hello my Rank is 0 processor 1 got Hello my Rank is 0
32
Communications point à point
All to one point to point Exercice : Modifier le script one_sided.c à un autre nommé all_to_one.c pour que tous les workers envoient un message d’introduction au master
33
Communications point à point
Solution : int main (int argc, char *argv[]) { int nbProc, myRank, tag, source, destination, i, length=30 ; char sbuf[30], rbuf[30] ; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); //envoie du message par les workers au master if (myRank != 0) { sprintf(sbuf,"Hello From Processor%d",myRank); MPI_Send(&sbuf, length, MPI_CHAR, 0, 0, MPI_COMM_WORLD); }
34
Communications point à point
Suite: else { //reception de tous les messages workers par le master for (i=0 ; i < nbProc ; i++){ MPI_Recv(&rbuf, length,MPI_CHAR, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); printf("Processor %d got %s\n" , myRank , rbuf); }} MPI_Finalize(); } Exécution : mpirun -np 4 res Processor 0 got Hello From Processor 1 Processor 0 got Hello From Processor 2 Processor 0 got Hello From Processor 3
35
Communications point à point
Communication et traitement Exercice : Créer un programme MPI ou plusieurs workers envoient un nombre entier (son rang) au master, le master somme les entiers reçus et affiche le résultat. Changer le programme pour que le master envoie la somme calculée à chaque worker.
36
Communications point à point
Solution : int main (int argc, char *argv[]) { int nbProc, myRank, i, sum_recv, sum = 0; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); if (myRank != 0) { // workers envoient leur rang au master MPI_Send(&myRank,1,MPI_INT,0,0,MPI_COMM_WORLD) ; // workers reçoivent la somme calculée par le master MPI_Recv(&sum_recv,1,MPI_INT,0,1,MPI_COMM_WORLD,MPI_STATUS_IGNORE); printf(“Iam worker %d I receive %d\n", myRank, sum_recv); }
37
Communications point à point
Suite : else { for ( i=1 ; i<nbProc ; i++) { MPI_Recv (&rank,1,MPI_INT, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); sum = sum+rank; } printf("My Rank is %d Sum Calculated is %d\n" , myRank , sum); for ( i=1 ; i<nbProc ; i++){ MPI_Send (&sum,1,MPI_INT, i,1, MPI_COMM_WORLD); }} MPI_Finalize();
38
Communications point à point
Exécution : mpirun -np 5 result MyRank is 1 I receive 10 MyRank is 2 I receive 10 MyRank is 3 I receive 10 MyRank is 4 I receive 10 My Rank is 0 Sum Calculated is 10
39
Communications point à point
Empaquetage des données Les fonctions MPI_Pack et MPI_Unpack permettent le transfert de plusieurs variables de types différents lors d'une seule communication MPI. int MPI_Pack ( void *inbuf, int incount, MPI_Datatype datatype, void *outbuf, int outcount, int *position, MPI_Comm comm ) int MPI_Unpack ( void *inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm )
40
Communications point à point
Exercice : Utilisez le type MPI_PACKED et les primitives MPI_Pack et MPI_Unpack pour créer un programme ou chaque worker envoie au master son rang ainsi qu’ un autre nombre réel. Le master affiche toutes les données reçues.
41
Communications point à point
Solution : int main (int argc, char *argv[]) { int nbProc, myRank, rank, i, pos ; float a ; char buf[10] ; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); if (myRank != 0) { a=(float) rand()/(float) RAND_MAX ; // générer un réel avec rand() pos=0 ; // empaqueter le rang et le réel généré MPI_Pack(&myRank,1, MPI_INT,buf,10,&pos,MPI_COMM_WORLD); MPI_Pack(&a,1,MPI_FLOAT,buf,10,&pos,MPI_COMM_WORLD); // envoyer le pack au master MPI_Send(&buf,10,MPI_PACKED,0,0,MPI_COMM_WORLD); }
42
Communications point à point
Suite : else { printf("My Rank is %d I Receive \n ",myRank ); for (i=1 ; i < nbProc ; i++){ // recevoir les packs des workers MPI_Recv (&buf,10, MPI_PACKED, i ,0 , MPI_COMM_WORLD, MPI_STATUS_IGNORE); pos=0; // dépaqueter les nombres MPI_Unpack(&buf,10,&pos,&rank,1,MPI_INT,MPI_COMM_WORLD); MPI_Unpack(&buf,10,&pos,&a,1,MPI_FLOAT,MPI_COMM_WORLD); printf(" %d and %f\n ", rank , a ); }} MPI_Finalize(); }
43
Communications collectives
Permettent de faire en une seule opération une série de communications point à point. Diffusion générale : MPI_Bcast() MPI_BCAST(message, length, type, rang_source, comm) Envoi d’un message à partir de l’adresse message de longueur length de type type, par le processus rang_source, à tous les autres processus du communicateur comm Réception de ce message à l’adresse message pour les processus autre que rang_source.
44
Communications collectives
Exercice : Utiliser la routine MPI_Bcast() pour créer un programme ou le master diffuse un message (Hello from master) à tous les workers du programme, chaque worker y compri le master affiche le message reçu
45
Communications collectives
Solution : int main (int argc, char *argv[]) { int taskid,nbrtask; char buffer[20] ; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &taskid); MPI_Comm_size(MPI_COMM_WORLD, &nbrtask); if (taskid == 0) { // préparer le message à envoyer strcpy(buffer, "Hello from master"); }
46
Communications collectives
Suite : // le master effectue la diffusion du message contenu à l’adresse buffer MPI_Bcast(&buffer,20,MPI_CHAR,0,MPI_COMM_WORLD); // afficher le contenu du buffer après broadcast printf(" processor %d got %s\n",taskid,buffer); MPI_Finalize(); }
47
Communications collectives
Exécution : mpirun -np 4 res processor 3 got Hello from master processor 0 got Hello from master processor 1 got Hello from master processor 2 got Hello from master
48
Communications collectives
Diffusion sélective : MPI_SCATTER()
49
Communications collectives
MPI_SCATTER(message_a_repartir, longueur_message_emis, type_message_emis, message_recu, longueur_message_recu, type_message_recu, rang_source, comm) Distribution, par le processus rang_source, à partir de l’adresse message_a_repartir, d’un message de taille longueur_message_emis, de type type_message_emis, à tous les processus du communicateur comm. Réception du message à l’adresse message_recu, de longueur longueur_message_recu et de type type_message_recu par tous les processus du communicateur comm.
50
Communications collectives
Exercice : Considérons un vecteur à N entier. Le but est de diviser le vecteur à plusieurs partitions autant qu’il y a de processus (m) dans la machine d’exécution parallèle. Chaque processus y compri le master, reçoit une partition du vecteur. Si N n’est pas multiple de m alors le vecteur doit être complété par des éléments artificiels complémentaires. 1- Créer un programme MPI (version1) pour effectuer un tel partitionnement en utilisant MPI_Send et MPI_Recv. 2- Créer un autre programme MPI (version2) pour effectuer un tel partitionnement en utilisant MPI_Scater 3- Utilisez la fonction MPI_Wtime pour mesurer et comparer le temps d’ exécution des deux versions.
51
Communications collectives
Solution avec MPI_Send et MPI_Recv int main (int argc, char *argv[]) { int v[n], n=20; int myRank, step, start, mod, m, i, a; double endtime, starttime; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &m); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); //calculer le nombre d’élément par partition et compléter le vecteur si n n’est pas multiple de m mod = n%m ; if ( mod != 0 ){ a = m-mod; n = n+a; step=n/m; }
52
Communications collectives
Suite : else { step=n/m ; } //le master envoie une partie du vecteur aux processus y compris lui if (myRank == 0) { //Remplir le vecteur v par des valeurs starttime= MPI_Wtime(); // Temps initial for (i=0; i<n; i++) { v[i]=i ; } start = 0 ; for (i=0; i<m; i++) { // envoyer à chaque processus l’adresse début de sa partition MPI_Send(&start,1,MPI_INT, i, 0, MPI_COMM_WORLD); // envoyer à chaque processus sa partition du vecteur v MPI_Send(&v[start], step, MPI_INT, i, 1, MPI_COMM_WORLD); start = start+step; }
53
Communications collectives
Suite : // master reçoit sa partition et l’affiche MPI_Recv(&start,1,MPI_INT,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE); MPI_Recv(&v, step, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); endtime = MPI_Wtime(); // Temps final printf ("Iam %d I Start From I Receive [ %d\n ", myRank, start); for (i=0; i<step; i++) { printf ("%d\n ",v[i]); } printf ("] "); printf("That took %f seconds\n", endtime-starttime); }
54
Communications collectives
Suite : else { // les processus reçoivent leurs partitions du vecteur v et les affichent starttime= MPI_Wtime(); // Temps initial MPI_Recv(&start,1,MPI_INT,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE); MPI_Recv(&v, step, MPI_INT, 0,1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); endtime = MPI_Wtime(); // Temps final printf ("Iam %d I Start From %d\n I Receive [ ", myRank, start); for (i=0; i<step; i++) { printf ("%d\n ",v[i]) ; } printf ("] "); MPI_Finalize(); }
55
Communications collectives
Exécution : mpirun -np 4 res Iam Process 3 I Start From 15 I receive [15 , 16 , 17 , 18 , 19 , ] That took seconds Iam Process 0 I Start From 0 I receive [0 , 1 , 2 , 3 , 4 , ] That took seconds Iam Process 1 I Start From 5 I receive [5 , 6 , 7 , 8 , 9 , ] That took seconds Iam Process 2 I Start From 10 I receive [10 , 11 , 12 , 13 , 14 , ] That took seconds
56
Communications collectives
Solution avec MPI_Scatter() : …………………………………..// début de programme inchangé if (myRank == 0) {//Remplir le vecteur v par des valeurs for (i=0; i<n; i++){ v[i]=i ; }} int vr[step]; // adresse du message reçu starttime = MPI_Wtime(); // temps initial // découper v selon le step et envoyer à tous les processus leur part MPI_Scatter(&v, step, MPI_INT, &vr, step, MPI_INT, 0, MPI_COMM_WORLD); endtime = MPI_Wtime(); printf ("I am Process %d\n I Receive [ ",myRank); for (i=0; i<step; i++) { // chaque processus affiche le sous vecteur reçu printf ("%d\n ",vr[i]); } printf ("] "); printf("That took %f seconds\n", endtime-starttime); MPI_Finalize(); }
57
Communications collectives
Exécution : mpirun -np 4 res I am Process 0 I Receive [ 0 ,1 ,2 ,3 ,4 ,]That took seconds I am Process 1 I Receive [ 5 ,6 ,7 ,8 ,9 ,]That took seconds I am Process 2 I Receive [ 10 ,11 ,12 ,13 ,14 ,]That took seconds I am Process 3 I Receive [ 15 ,16 ,17 ,18 ,19 ,]That took seconds
58
Communications collectives
Réduction Une opération appliquée à un ensemble d’éléments pour en obtenir une seule valeur exemple : la somme des valeurs envoyées par les processus, recherche de max ou min des éléments envoyés …
59
Communications collectives
Exercice : Etendre les deux versions précédentes pour que chaque processus calcule la somme partielle de son sous vecteur et envoyer son résultat au processus maitre qui à son tour somme les sommes partielles et affiche le résultat. Pour la version 2 utilisez la fonction MPI_Reduce Comparer le temps d’exécution des deux versions
60
Communications collectives
Solution version1: if (myRank == 0) { ……………………………….. // voir programme version1 // master reçoit son sous vecteur MPI_Recv(&v, step, MPI_INT, 0,1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); printf("Iam Process %d\n", myRank); sumt=0 ; for (i=0 ; i<step ; i++) {// master calcule sa somme partielle et l’affiche sumt=sumt+v[i] ; printf ("%d , ",v[i]); } printf("] my Partial Sum is %d\n", sumt) ;
61
Communications collectives
Suite version1: // le master reçoit les sommes partielles et calcule la somme totale for(i=1; i<m; i++){ MPI_Recv ( &sump,1, MPI_INT, i, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE); sumt= sumt+sump; endtime = MPI_Wtime(); //Temps final printf(" Partial Sum received from %d is %d\n" , i, sump); else { // workers reçoivent leurs sous vecteurs MPI_Recv ( &v, step, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
62
Communications collectives
Suite version1: // workers affichent le sous vecteur reçu et calculent la somme partielle sump=0; printf("Iam %d \n", myRank); for (i=0 ; i<step ; i++) { sump=sump+v[i] ; printf ("%d , ",v[i]) ; } printf ("]\n "); // workers envoient la somme partielle calculée MPI_Send (&sump,1, MPI_INT, 0, 2, MPI_COMM_WORLD); } MPI_Finalize(); }
63
Communications collectives
Exécution : mpirun -np 6 res Iam Process 0 I Receive [ 0 ,1 ,2 ,3 ,] My Parial Sum is 6 Partial Sum received from 1 is 22 Partial Sum received from 2 is 38 Partial Sum received from 3 is 54 Partial Sum received from 4 is 70 Partial Sum received from 5 is 86 Total Sum is 276 That took seconds Iam Process 1 I Receive [ 4 , 5 , 6 , 7 , ] Iam Process 2 I Receive [ 8 , 9 , 10 , 11 , ] Iam Process 3 I Receive [ 12 , 13 , 14 , 15 , ] Iam Process 4 I Receive [ 16 , 17 , 18 , 19 , ] Iam Process 5 I Receive [ 20 , 21 , 22 , 23 , ]
64
Communications collectives
Solution version2 : ………………………….. // voir programme version2 printf ("I am Process %d\n ",myRank); Psum=0; for (i=0 ; i<step ; i++) { //chaque processus affiche le sous vecteur reçu et calcule la somme partielle printf ("I Receive %d\n ", vr[i] ); psum=psum+vr[i]; } printf ("partial sum= %d\n ",psum); //MPI_Reduce pour calculer la somme globale et l’afficher par le master MPI_Reduce ( &psum, &gsum,1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); endtime = MPI_Wtime(); // Temps final if (myRank==0){ printf ("I am %d I Receive the Globale Sum of %d\n ", myRank, gsum); } MPI_Finalize() ; }
65
Communications collectives
Exécution : mpirun -np 6 res I am Process 0 I Receive [ 0 , 1 , 2 , 3 , ] Partial sum= 6 Globale Sum is 276 That took seconds I am Process 1 I Receive [ 4 , 5 , 6 , 7 , ] Partial sum= 22 I am Process 2 I Receive [ 8 , 9 , 10 , 11 , ] Partial sum= 38 I am Process 3 I Receive [ 12 , 13 , 14 , 15 , ] Partial sum= 54 I am Process 4 I Receive [ 16 , 17 , 18 , 19 , ] Partial sum= 70 I am Process 5 I Receive [ 20 , 21 , 22 , 23 , ] Partial sum= 86
66
Communications collectives
MPI_Gather :
67
Communications collectives
MPI_Gather ( message_emis, longueur_message_emis, type_message_emis, message_recu, longueur_message_recu, type_message_recu, rang_dest, comm) Envoi de chacun des processus du communicateur comm, d’un message message_emis, de taille longueur_message_emis et de type type_message_emis Collecte de chacun de ces messages, par le processus rang_dest, à partir l’adresse message_recu, sur une longueur longueur_message_recu et avec le type type_message_recu
68
Communications collectives
Exercice : Etendre la version 2 (sans réduction somme) pour que chaque processus modifie son sous vecteur reçu en ajoutant un nombre fixe à tous les éléments du sous vecteur ensuite le master rassemble les sous vecteurs modifiés. Utilisez les fonctions MPI_Scatter et MPI_Gather.
69
Communications collectives
Solution : starttime= MPI_Wtime(); //temps initial MPI_Scatter ( &v, step,MPI_INT, &vr, step, MPI_INT, 0, MPI_COMM_WORLD); //endtime = MPI_Wtime(); for ( i=0; i<step; i++ ) { //chaque processus reçoit son sous vecteur et ajoute le nombre m à tous ses éléments vr[i]=vr[i]+m; } //master rassemble tous les sous vecteurs modifiés par chaque processus MPI_Gather ( &vr, step, MPI_INT, &back, step, MPI_INT, 0, MPI_COMM_WORLD); endtime = MPI_Wtime(); // Temps final if (myRank == 0){ // master affiche les sous vecteurs rassemblés printf("The Global Vector Received from All Process is\n");
70
Communications collectives
for (i=0; i<(m*step) ; i++) { printf (" %d ", back[i] ) ; } printf("\nThat took %f seconds\n",endtime-starttime); } MPI_Finalize() ; } Exécution : mpirun –np 6 res The Global Vector Received from All Process is
71
OpenMP Un ensemble de procédures et de directives de compilation identifiées par un mot clef initial !$OMP visant à réduire le temps de restitution lors de l’exécution d’un programme sans en changer la sémantique OpenMP est un modèle de programmation parallèle basé sur les architectures à mémoire partagée. Les taches de calcul peuvent accéder à un espace mémoire commun, ce qui limite la redondance des données et simplifie les échanges d’information entre les taches. En pratique, la parallélisation repose sur l’utilisation de processus système légers (ou threads), on parle alors de programme multithreadé
72
Environnement Compilateurs supportant OpenMP (libgomp)
A partir de GCC 4.7.0, OpenMP 3.1 est complètement supporté. Dans GCC 4.9.0, OpenMP 4.0 est supporté pour C et C++, mais pas le Fortran. A partir GCC 4.9.1, OpenMP 4.0 est complètement supporté. Pour plus d’informations sur les compilateurs supportés voir lien:
73
Concepts générales
74
Concepts générales Thread : Entité d’exécution avec une mémoire locale (stack) Les taches d’un même programme partagent l’espace mémoire de la tache initiale (mémoire partagée) mais disposent aussi d’un espace mémoire local : la pile (stack). Il est possible de définir des variables partagées (stockées dans la mémoire partagée) ou des variables privées (stockées dans la pile de chacune des taches).
75
Fonctionnalités OpenMP proposent des mécanismes pour :
Partager le travail entre les taches. Il est par exemple possible de répartir les itérations d’une boucle entre les taches. Partager ou privatiser les variables. Synchroniser les threads.
76
Construction d’une région parallèle
Un programme OpenMP est une alternance de régions séquentielles et parallèles (modèle ≪ fork and join ≫) A l’entrée d’une région parallèle, le thread maitre (celui de rang 0) active (fork) des processus fils Ces processus fils exécutent leur tache implicite puis disparaissent ou s’assoupissent en fin de région parallèle (join). En fin de région parallèle, l’exécution redevient séquentielle avec uniquement l’exécution du thread maitre.
77
Syntaxe générale d’une directive
Pour utiliser les fonctions OpenMP il faut inclure Le module OMP _LIB pour Fortran Le fichier omp.h pour C/C++ Une directive OpenMP possède la forme suivante : Pour C et C++ : #include <omp.h> ... #pragma omp parallel private(a,b) firstprivate(c,d,e) { ... }
78
Syntaxe générale d’une directive
Pour Fortran: !$ use OMP_LIB ... ! $OMP PARALLEL PRIVATE(a,b) & ! $OMP FIRSTPRIVATE(c,d,e) ! $OMP END PARALLEL
79
Compilation et exécution
les options de compilation permettent d’activer l’interprétation des directives OpenMP par certains compilateurs : Compilateur GNU : -fopenmp gfortran -o omp_hellof -fopenmp omp_hello.f gcc -o omp_helloc -fopenmp omp_hello.c Compilateur Intel : -openmp icc -o omp_helloc -openmp omp_hello.c ifort -o omp_hellof -openmp omp_hello.f Exécution : export OMP_NUM_THREADS =4 # Nombre de threads ./ omp_helloc
80
OpenMP Exemple : Ouvrir un fichier nommé hello_word.c et copier le code suivant de dans #include <omp.h> #include <stdio.h> #include <stdlib.h> Int main (int argc, char *argv[]) { #pragma omp parallel { printf( "Hello OpenMP" ); } } Compiler le code avec la commande : gcc –O3 –o hello_word -fopenmp hello_word.c
81
OpenMP Suite : Définir le nombre des threads pour l’exécution en parallèle export OMP_NUM_THREADS=4 Exécuter ./hello_word Changer le nombre des threads, ré exécuter et vérifier le output Recompiler votre script sans l’option –fopenmp gcc –O3 –o hello_word hello_word.c Varie le nombre des threads et exécuter en vérifiant que la directive openmp n’est plus utilisée
82
OpenMP Exécution : ./hello_word Hello OpenMP
83
Variables privées et partagées
#pragma omp parallel private(nthreads, tid) Par défaut, les variables sont partagées. La clause PRIVATE permet de changer le statut d’une variable. Si une variable possède un statut privé, elle est allouée dans la pile de chaque tache. Les variables privées ne sont pas initialisées à l’entrée d’une région parallèle mais grâce à la clause FIRSTPRIVATE, il est possible de forcer l’initialisation d’une variable privée à la dernière valeur qu’elle avait avant l’entrée dans la région parallèle.
84
Informations des threads
La fonction omp_get_thread_num() permet de retourner le id du thread La fonction omp_get_num_threads() retourne le nombre des threads respectivement
85
Variables privées et partagées
Exemple : int main (int argc, char *argv[]) { int nthreads, tid, a=1000 ; // Fork un ensemble de threads en donnant à chacun sa copie de variables #pragma omp parallel private(nthreads, tid, a) { tid = omp_get_thread_num(); nthreads = omp_get_num_threads(); a=a+20; printf("Hello my thread id is = %d out of %d a=%d\n", tid,nthreads,a); } printf("After parallel region a = %d\n",a );
86
Variables privées et partagées
Exécution : ./thread_info Hello my thread id is = 0 out of 4 a=20 Hello my thread id is = 2 out of 4 a=20 Hello my thread id is = 3 out of 4 a=20 Hello my thread id is = 1 out of 4 a=20 After parallel region a = 1000 Forcer l’initialisation de la valeur “a” et ré exécuter le programme Hello my thread id is = 3 out of 4 a=1020 Hello my thread id is = 0 out of 4 a=1020 Hello my thread id is = 2 out of 4 a=1020 Hello my thread id is = 1 out of 4 a=1020
87
Boucle parallèle un parallélisme par répartition des itérations d’une boucle. La boucle parallélisée est celle qui suit immédiatement la directive DO. Le mode de répartition des itérations peut être spécifié dans la clause SCHEDULE. Les boucles infinies et do while ne sont pas parallélisables avec cette directive Les indices de boucles sont par défaut des variables entières privées, pas besoin de spécifier le statut. Il est possible d’introduire autant de constructions DO (les unes après les autres) qu’il est souhaité dans une région parallèle.
88
Boucle parallèle Exercice : copier le code suivant dans un fichier nommé loop.c int main (int argc, char *argv[]) { int tid, num_loops, i , n=1000 ; #pragma omp parallel private(tid) { tid = omp_get_thread_num() ; num_loops=0 ; #pragma omp for for (i=1; i<n ; i++) num_loops = num_loops+1; printf ("Hello my thread id is = %d number of loops %d\n", tid,num_loops) ; } printf("total loops %d",num_loops); }
89
Boucle parallèle Suite : Compiler et exécuter le programme avec 4 threads. Est-ce que le nombre de boucles est cohérent ? Changer le statu de la variable num_loops à private et ré exécuter . La valeur num_loops n’a pas de sens hors la région parallèle , pour avoir la somme du nombre de boucles utilisez la directive reduction qui permet de sommer les valeurs de la variable privée num_loops en fin de la région parallèle Supprimer num_loops de de la directive private et ajouter reduction( +:num_loops) après la directive private
90
Hello my thread id is = 1 number of loops 250
Boucle parallèle Exécution1 : ./loop Hello my thread id is = 1 number of loops 250 Hello my thread id is = 0 number of loops 250 Hello my thread id is = 2 number of loops 250 Hello my thread id is = 3 number of loops 250 total loops 250 Exécution2 : Hello my thread id is = 3 number of loops 249 total loops 0
91
Boucle parallèle Exécution 3 : Hello my thread id is = 1 number of loops 250 Hello my thread id is = 2 number of loops 250 Hello my thread id is = 0 number of loops 250 Hello my thread id is = 3 number of loops 249 total loops 999
92
Scheduling Mode de distribution des itérations d’une boucle parallèle Le choix du mode de répartition permet de mieux contrôler l’équilibrage de la charge de travail entre les threads. STATIC consiste à diviser les itérations en paquets d’une taille donnée et attribuer de façon cyclique à chacun des threads, un ensemble de paquets suivant l’ordre des threads DYNAMIC les itérations sont divisées en paquets de taille donnée. Sitot qu’un thread épuise les itérations de son paquet, un autre paquet lui est attribué GUIDED similaire au dynamic mais la taille des paquets décroit ,tous les paquets ont une taille supérieure ou égale à une valeur donnée à l’exception du dernier dont la taille peut être inférieure(par défaut la valeur vaut nbr_loop/nbr_threa).
93
Scheduling Exercice : Utiliser le programme précédent en changeant le Scheduling #pragma omp do schedule(run time) Compiler et exécuter le programme en variant le nombre de threads et le Sheduling Export OMP_SCHEDULE=static ,dynamic ou guided Exécution : Static Hello my thread id is = 0 number of loops 250 Hello my thread id is = 2 number of loops 250 Hello my thread id is = 1 number of loops 250 Hello my thread id is = 3 number of loops 249 total loops 999
94
Scheduling Dynamic Hello my thread id is = 2 number of loops 320 Hello my thread id is = 0 number of loops 253 Hello my thread id is = 1 number of loops 217 Hello my thread id is = 3 number of loops 209 total loops 999 Guided Hello my thread id is = 0 number of loops 429 Hello my thread id is = 2 number of loops 369 Hello my thread id is = 3 number of loops 73 Hello my thread id is = 1 number of loops 128
95
Réduction Opération associative appliquée à une variable partagée. L’opération peut être : ➳ arithmétique : +, –, ×; ➳ logique : AND , OR , EQV , NEQV ➳ une fonction intrinsèque : MAX, MIN Chaque thread calcule un résultat partiel indépendamment des autres. Ils se synchronisent ensuite pour mettre à jour le résultat final.
96
π/4 = arctn(1) = ∑ (-1)n / (2n+1)
Réduction Exercice : Créer un programme séquentiel pi_serial.c qui calcule la valeur de π avec la formule suivante( augmenter le nombre d’itération nstep pour avoir plus de précision) π/4 = arctn(1) = ∑ (-1)n / (2n+1) Copier le programme séquentiel dans un fichier nommé pi_omp.c et paralléliser avec une reduction de la boucle principale Utilisez la fonction time du c pour calculer le temps écoulé pour calculer π (utiliser la fonction time hors la région parallèle) n=nsteps n=0
97
Réduction Suite : Augmenter le nombre d’itérations jusqu’à ce que l’exécution du programme séquentiel atteint 60 secondes et comparer le temps en remontant le nombre des threads.
98
#include <time.h> // fonction qui calcule (-1)n
Réduction Solution : Serial #include <time.h> // fonction qui calcule (-1)n int powr (int n) { int i,pui=1; if (n==0) { pui=1; } for (i=1; i<=n; i++) pui = pui * -1; return pui; }
99
int main (int argc, char *argv[]) { int j, n=100000 ; double sum, pi;
Réduction Suite : int main (int argc, char *argv[]) { int j, n= ; double sum, pi; clock_t a, b ; float c ; sum=0; a=time(NULL); // temps de début for(j=0;j<=n;j++){ // calculer la somme pi sum=sum+((double)powr(j)/(double)(2*j+1)); } b=time(NULL); // fin d’exécution c=(float)(b-a); // temps écoulé
100
printf(" pi=%lf\n" ,pi); } Exécution :
Réduction Suite : printf(" c=%f\n" ,c); pi=sum*4; printf(" pi=%lf\n" ,pi); } Exécution : pi value is Elapsed time is sec
101
Réduction Parallel ……… // inchangé #pragma omp parallel for reduction(+:sum) //#pragma omp for for(j=0 ; j<=n ; j++){ sum=sum+((double)powr(j)/(double)(2*j+1)); } Exécution : pi value = Elapsed time is
102
Performance parallèle
Pour écrire une application parallèlle il faut vérifier l’éfficacité du parallélisme Pour cela il faut inclure les fonctions du temps en augmentant à chaque fois le nombre de processeurs et vérifier l’éfficacité Exercice : Ecrire un code séquentiel matmul_serial.c qui calcule le produit de deux matrices C=A*B Copier le code séquentiel dans fichier nommé matmul_omp.c et utilisez OpenMP pour paralléliser et vérifier que les résultats des deux codes sont similaires Exécuter avec 1 et 2 threads , comparer les deux
103
Performance parallèle
Suite : Pour avoir plus de puissance , utiliser le cluster pour réserver un noeud à 8 processeurs et exécuter le code en variant la taille du matrice 2000, exécuter avec 1,2,4,6 et 8 processeurs Remplir le tableau suivant : Nombre de processeurs 1 2 4 6 8 Temps Exécution SP Efficacité
104
Performance parallèle
Suite : Déssiner le graphe speedup Sp Le speedup est donné par la formule Sp=T1/ Tp P nbr processeurs T1 temps d’exécution séquentiel Tp temps d’exécution parallel avec p processeurs Un speedup linéaire ou idélae est obtenu quand Sp=p Déssiner le graphe Efficacité Ep=Sp/p = T1/p * Tp / 0<Ep<1
105
Performance parallèle
Solution : Serial #define N 1000 int A[N][N], B[N][N], C[N][N]; // declarer matrices NxN int main () { /* DECLARING VARIABLES */ int i, j, m; // indices de matrix multiplication float t_1; // Execution time measures clock_t c_1, c_2; // remplir A et B avec la fonction rand() srand ( time(NULL) ); // initialise le générateur des nombres rundum for(i=0;i<N;i++) { for(j=0;j<N;j++) { A[i][j]= (rand()%10); B[i][j]= (rand()%10); } }
106
Performance parallèle
Suite: Serial c_1=time(NULL); // temps début for(i=0;i<N;i++) { for(j=0;j<N;j++) { C[i][j]=0; // initialiser la matrice C à 0 for(m=0;m<N;m++) { C[i][j]=A[i][m]*B[m][j]+C[i][j]; } // calculer les C[i][j] } } c_2=time(NULL); // temps final t_1 = (float)(c_2-c_1); // time elapsed for job row-wise printf("Execution time: %f \n",t_1); return 0; }
107
Performance parallèle
Exécution : Serial ./ matmul_serial Execution time :
108
Performance parallèle
Solution : Parallel //inchangé #pragma omp parallel printf("Number of threads: %i \n",omp_get_num_threads()); c_1=time(NULL); // time measure: start mm #pragma omp parallel for private(m,j) for(i=0;i<N;i++) { for(j=0;j<N;j++) { C[i][j]=0; // set initial value of resulting matrix C = 0 for(m=0;m<N;m++) { C[i][j]=A[i][m]*B[m][j]+C[i][j]; } } }
109
Performance parallèle Exécution : Parallel ./matmul_omp
Number of threads: 2 Execution time: Nombre de processeurs 1 2 4 6 8 Temps Exécution 77 39 19 13 9 SP 1.97 4.05 5.92 8.55 Efficacité 0,985 1,0125 0,986 1,06875
110
Performance parallèle
Exécution : Parallel
111
Merci pour votre attention
Présentations similaires
© 2024 SlidePlayer.fr Inc.
All rights reserved.