Académique Documents
Professionnel Documents
Culture Documents
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
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();
}
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
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
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");
}
}
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
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);
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
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.
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