Vous êtes sur la page 1sur 17

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);
MPI_COMM_WORLD Le communicateur par défaut

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

Resultat
#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();
}

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);

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

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

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.

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

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

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;
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();
}
Exécution :
mpicc -o result one_sided.c
mpirun –np 4 result
processor 1 got hello my Rank is 0

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

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

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

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

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

// 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();
}
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
Diffusion sélective : MPI_SCATTER()
MPI_SCATTER(message_a_repartir,longueur_message_emis,type_message_emis,message_rec
u, 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.
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.
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;

}
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;
}
// 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);
}
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();
}
Exécution :
mpirun -np 4 res
Iam Process 3 I Start From 15 I receive [15 , 16 , 17 , 18 , 19 , ] That took 0.000071 seconds Iam
Process 0 I Start From 0 I receive [0 , 1 , 2 , 3 , 4 , ] That took 0.000072 seconds Iam Process 1 I
Start From 5 I receive [5 , 6 , 7 , 8 , 9 , ] That took 0.000067 seconds Iam Process 2 I Start From
10 I receive [10 , 11 , 12 , 13 , 14 , ] That took 0.000072 seconds

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);

Exécution :
mpirun -np 4 res
I am Process I Receive [ 0 ,1 ,2 ,3 ,4 ,]That took 0.000019 seconds I am Process 1 I Receive [ 5
,6 ,7 ,8 ,9 ,]That took 0.000022 seconds I am Process 2 I Receive [ 10 ,11 ,12 ,13 ,14 ,]That took
0.000025 seconds I am Process 3 I Receive [ 15 ,16 ,17 ,18 ,19 ,]That took 0.000026 seconds
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 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

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) ;
// 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);

// 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(); }
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 276That took 0.000101 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 , ]

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() ; }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 0.000051
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
MPI_Gather :
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’unmessage 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
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.

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");
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
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

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

SOURCE CHAT GPT DONC ATTENTION:


Voici un résumé bref et clair de quelques clauses OpenMP couramment utilisées :

1. private :
○ Déclare une variable comme privée pour chaque thread.
○ Chaque thread a sa propre copie de la variable.
○ Les modifications à la variable dans un thread n'affectent pas les autres threads.
2. shared :
○ Déclare une variable comme partagée entre tous les threads.
○ Tous les threads accèdent et modifient la même copie de la variable.
○ Utilisé pour partager des données entre les threads.
3. firstprivate :
○ Similaire à private, mais initialise chaque copie privée avec la valeur de la
variable avant la région parallèle.
○ Utile lorsque chaque thread doit travailler avec une copie privée de la variable ayant
la valeur initiale.
4. lastprivate :
○ Garantit que la variable privée d'un thread retient la valeur finale après la région
parallèle.
○ Souvent utilisé dans les boucles parallèles pour récupérer la valeur finale d'une
variable.
5. reduction :
○ Effectue une opération de réduction (comme la somme) sur une variable à travers
tous les threads.
○ Fournit un moyen d'agrégation des résultats individuels de chaque thread.
6. critical :
○ Encadre une section critique du code, garantissant qu'un seul thread à la fois peut
exécuter cette section.
○ Utilisé pour éviter les conflits de données partagées.
7. atomic :
○ Effectue une opération atomique sur une variable partagée.
○ Assure l'exécution sans interruption de l'opération, évitant les conflits de données.
8. threadprivate :
○ Déclare une variable privée pour chaque thread, mais conserve la valeur entre
différentes régions parallèles.
○ Utile lorsque la variable doit être privée pour chaque thread, mais son état doit être
préservé entre les régions parallèles.

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.

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

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.

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)
n=nsteps
π/4 = arctn(1) = ∑
(-1)n / (2n+1)
n=0
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).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.

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;
}
int main (int argc, char *argv[]) {
int j, n=100000 ;
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é
pi=sum*4;
printf(" pi=%lf\n" ,pi); }
Exécution :
pi value is 3.141603
Elapsed time is 10.000000 sec

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 =3.141603
Elapsed time is 3.000000

151Performance 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
152Performance 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é
153Performance 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
154Performance 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); } }
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;
}

Exécution : Serial
./ matmul_serial
Execution time : 7.000000
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];
}}}

Exécution : Parallel
./matmul_omp
Number of threads: 2
Number of threads: 2
Execution time: 3.000000

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

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

Exécution : Serial
./ matmul_serial
Execution time : 7.000000

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];
}}}

Exécution : Parallel
./matmul_omp
Number of threads: 2
Number of threads: 2
Execution time: 3.000000

Vous aimerez peut-être aussi