Utilisateur:Kabyhaswell/ProgrammationParallèleMPI/Exercices/Communications de base
Envois et réceptions simples
[modifier | modifier le wikicode]Exercice 2-1 : aller-retour
[modifier | modifier le wikicode]Écrivez un programme qui fonctionne avec un nombre quelconque de processus et dans lequel :
- le processus 0 tire un nombre entier aléatoirement et l’affiche
- le processus 0 envoie ce nombre au processus 1
- le processus 1 incrémente ce nombre
- le processus 1 envoie le résultat au processus 0
- le processus 0 affiche le résultat
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* pour getpid() */
#include <mpi.h>
#define TAG 42
int main( int argc, char** argv ) {
int rank, token;
MPI_Status status;
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
switch( rank ) { /* Seuls les processus 0 et 1 participent */
case 0:
srand( getpid() );
token = rand();
printf( "J'ai tiré %d\n", token );
/* On envoie le jeton au processus 1 */
MPI_Send( &token, 1, MPI_INT, 1, TAG, MPI_COMM_WORLD );
/* On reçoit le résultat du processus 1 */
MPI_Recv( &token, 1, MPI_INT, 1, TAG, MPI_COMM_WORLD, &status );
printf( "J\'ai reçu %d\n", token );
break;
case 1:
/* On reçoit le jeton du processus 0 et on l'incrémente */
MPI_Recv( &token, 1, MPI_INT, 0, TAG, MPI_COMM_WORLD, &status );
token++;
/* Et on lui envoie le résultat */
MPI_Send( &token, 1, MPI_INT, 0, TAG, MPI_COMM_WORLD );
break;
}
MPI_Finalize();
return EXIT_SUCCESS;
}
Exercice 2-2 : ping-pong
[modifier | modifier le wikicode]Écrivez un programme qui fonctionne avec un nombre quelconque et dans lequel les processus 0 et 1 s'échangent un tableau de nombres à virgule flottante. La taille du tableau est passée en argument du programme.
- Ce tableau est initialisé sur le rank 0 avec des valeurs aléatoires entre 0 et 1000.
- Le processus de rang 0 l’envoie au processus de rang 1.
- Le processus de rang 1 multiplie toutes les valeurs par 2 et renvoie le résultat au processus de rang 0.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* pour getpid() */
#include <mpi.h>
#define TAG 42
double myrand( double );
void initTab( double**, int, int );
void computeTab( double*, int );
int main( int argc, char** argv ) {
int rank, tabsize;
double* data;
MPI_Status status;
/* Initialisation */
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
if( argc < 1 ) {
if( 0 == rank )
printf( "Veuillez passer la taille du tableau en argument de la ligne de commande\n" );
MPI_Finalize();
return EXIT_SUCCESS;
}
tabsize = atoi( argv[1] );
if( rank > 1 ) { /* Seuls les processus 0 et 1 participent */
MPI_Finalize();
return EXIT_SUCCESS;
}
initTab( &data, tabsize, rank );
if( 0 == rank ) {
/* On envoie le tableau au processus 1 */
MPI_Send( data, tabsize, MPI_DOUBLE, 1, TAG, MPI_COMM_WORLD );
/* On reçoit le tableau du processus 1 */
MPI_Recv( data, tabsize, MPI_DOUBLE, 1, TAG, MPI_COMM_WORLD, &status );
} else { /* rang 1 */
/* On reçoit le tableau du processus 0 et on fait l'opération */
MPI_Recv( data, tabsize, MPI_DOUBLE, 0, TAG, MPI_COMM_WORLD, &status );
computeTab( data, tabsize );
/* Et on lui envoie le résultat */
MPI_Send( data, tabsize, MPI_DOUBLE, 0, TAG, MPI_COMM_WORLD );
}
free( data );
MPI_Finalize();
return EXIT_SUCCESS;
}
double myrand( double max ){
return ( (double) rand() / (double) RAND_MAX ) * max;
}
void initTab( double** tab, int taille, int rank ) {
int i;
*tab = (double*) malloc( taille * sizeof( double ) );
if( 0 == rank ) { /* initialisation sur le rang 0 */
srand( getpid( ) );
for( i = 0 ; i < taille ; i++ ) {
(*tab)[i] = myrand( 1e3 );
}
}
}
void computeTab( double* tab, int taille ) {
int i;
for( i = 0 ; i < taille ; i++ ) {
tab[i] *= 2 ;
}
}
Modifiez maintenant votre programme pour que cet échange se fasse 50 fois.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* pour getpid() */
#include <mpi.h>
#define TAG 42
#define NBITER 50
double myrand( double );
void initTab( double**, int, int );
void computeTab( double*, int );
int main( int argc, char** argv ) {
int rank, tabsize, i;
double* data;
MPI_Status status;
/* Initialisation */
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
if( argc < 1 ) {
if( 0 == rank )
printf( "Veuillez passer la taille du tableau en argument de la ligne de commande\n" );
MPI_Finalize();
return EXIT_SUCCESS;
}
tabsize = atoi( argv[1] );
if( rank > 1 ) { /* Seuls les processus 0 et 1 participent */
MPI_Finalize();
return EXIT_SUCCESS;
}
initTab( &data, tabsize, rank );
for( i = 0 ; i < NBITER ; i++ ) {
if( 0 == rank ) {
/* On envoie le tableau au processus 1 */
MPI_Send( data, tabsize, MPI_DOUBLE, 1, TAG, MPI_COMM_WORLD );
/* On reçoit le tableau du processus 1 */
MPI_Recv( data, tabsize, MPI_DOUBLE, 1, TAG, MPI_COMM_WORLD, &status );
} else { /* rang 1 */
/* On reçoit le tableau du processus 0 et on fait l'opération */
MPI_Recv( data, tabsize, MPI_DOUBLE, 0, TAG, MPI_COMM_WORLD, &status );
computeTab( data, tabsize );
/* Et on lui envoie le résultat */
MPI_Send( data, tabsize, MPI_DOUBLE, 0, TAG, MPI_COMM_WORLD );
}
}
free( data );
MPI_Finalize();
return EXIT_SUCCESS;
}
double myrand( double max ){
return ( (double) rand() / (double) RAND_MAX ) * max;
}
void initTab( double** tab, int taille, int rank ) {
int i;
*tab = (double*) malloc( taille * sizeof( double ) );
if( 0 == rank ) { /* initialisation sur le rang 0 */
srand( getpid( ) );
for( i = 0 ; i < taille ; i++ ) {
(*tab)[i] = myrand( 1e3 );
}
}
}
void computeTab( double* tab, int taille ) {
int i;
for( i = 0 ; i < taille ; i++ ) {
tab[i] *= 2 ;
}
}
Exercice 2-3 : cherchez l’erreur
[modifier | modifier le wikicode]Considérez le programme suivant. Quel est le problème ?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* pour getpid() */
#include <mpi.h>
#define TAG 42
int main( int argc, char** argv ) {
int rank, token;
MPI_Status status;
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
if( rank > 1 ) { /* Seuls les processus 0 et 1 participent */
MPI_Finalize();
return EXIT_SUCCESS;
}
srand( getpid() );
token = rand();
MPI_Send( &token, 1, MPI_INT, rank ^ 0x1, TAG, MPI_COMM_WORLD );
MPI_Recv( &token, 1, MPI_INT, rank ^ 0x1, TAG, MPI_COMM_WORLD, &status );
MPI_Finalize();
return EXIT_SUCCESS;
}
Les deux processus font chacun un envoi : donc à chaque envoi, il n'y a pas de réception qui corresponde. On est dans une situation de deadlock.
En réalité, ce programme fonctionne-t-il ? Pourquoi ?
On n'envoie qu'un entier : les messages communiqués sont de petite taille. Il y a donc de fortes chances pour que les messages soient envoyés en mode eager, même si cela dépend de la configuration de la bibliothèque. Donc les MPI_Send
retournent dès que leur message est copié dans la couche de communication, qui l’envoie et le processus de réception met le message dans un buffer de réception puis le reçoit quand la fonction MPI_Recv
est appelée.
Même si le programme semble fonctionner, c'est une très mauvaise façon de faire, et il faut considérer que tout MPI_Send
qui n'a pas de MPI_Recv
correspondant est faux.
Modifiez le programme pour utiliser un MPI_Ssend
à la place du MPI_Send
. Le programme fonctionne-t-il ?
La fonction MPI_Ssend
est un envoi synchrone : la communication ne se fait que si le destinataire est dans le MPI_Recv
correspondant. Ici ce n’est pas le cas, car le destinataire est lui aussi dans un MPI_Ssend
. On a bien un deadlock.
Modifiez ce programme pour qu'il fonctionne.
Il suffit de faire les communications dans un ordre différent sur les deux processus. L'un fait d'abord l'envoi puis la réception, l’autre fait d'abord la réception puis l’envoi. Ainsi, chaque envoi correspond à une réception du côté du destinataire.
#include <stdio.h>
if( rank ) {
MPI_Ssend( &token, 1, MPI_INT, rank ^ 0x1, TAG, MPI_COMM_WORLD );
MPI_Recv( &token, 1, MPI_INT, rank ^ 0x1, TAG, MPI_COMM_WORLD, &status );
} else {
MPI_Recv( &token, 1, MPI_INT, rank ^ 0x1, TAG, MPI_COMM_WORLD, &status );
MPI_Ssend( &token, 1, MPI_INT, rank ^ 0x1, TAG, MPI_COMM_WORLD );
}
Exercice 2-4 : anneau à jeton
[modifier | modifier le wikicode]Le but de cet exercice est de faire circuler un anneau entre les processus. Le jeton est injecté par le processus de rang 0 et initialisé à la valeur 0. Chaque processus qui reçoit l’anneau de son voisin de rang inférieur l'incrémente et le transmet à son voisin de rang supérieur. Pour des raisons de simplicité, la rotation s'arrête au rang 1, quand le processus de rang 1 arrête de transmettre le jeton et affiche sa valeur. Le jeton doit faire un nombre arbitraire de tours de l’anneau, par exemple 16.
Vous pourrez par exemple, dans un premier temps, utiliser MPI_Ssend
pour débugger.
Attention : on a la valeur du voisin de rang supérieur avec (rank + 1) %size
, mais, comme le comportement du modulo d'un nombre négatif n'est pas forcément défini suivant le langage de programmation ou son implémentation, on utilise ( rank + size - 1) % size
pour avoir le voisin de rang inférieur.
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#define TAG 42
#define NBTOURS 16
int main( int argc, char** argv ) {
int rank, size, token, i;
MPI_Status status;
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Comm_size( MPI_COMM_WORLD, &size );
if( 0 == rank ) { /* Injection du jeton dans l'anneau */
token = 0;
MPI_Send( &token, 1, MPI_INT, 1, TAG, MPI_COMM_WORLD );
}
for( i = 0 ; i < NBTOURS ; i++ ) {
MPI_Recv( &token, 1, MPI_INT, ( rank + size - 1 ) % size, TAG, MPI_COMM_WORLD, &status );
token++;
MPI_Send( &token, 1, MPI_INT, ( rank + 1 ) % size, TAG, MPI_COMM_WORLD );
}
if( 1 == rank ) { /* Fin de la rotation */
MPI_Recv( &token, 1, MPI_INT, 0, TAG, MPI_COMM_WORLD, &status );
printf( "Valeur du jeton: %d\n", token );
}
MPI_Finalize();
return EXIT_SUCCESS;
}
Exercice 2-5 : comptage des nombres premiers
[modifier | modifier le wikicode]Le but de cet exercice est de compter le nombre de nombres premiers dans un intervalle. Pour cela, nous allons utiliser un algorithme très naïf qui compte le nombre de nombres premiers dans un intervalle, et découper l'intervalle en sous-intervalles. Chaque processus va calculer le nombre de nombres premiers dans un sous-intervalle, et le processus de rang 0 va sommer ces nombres pour avoir le nombre de nombres premiers dans l'intervalle global. C'est une méthode de type "diviser pour mieux régner".
La fonction de comptage des nombres premiers que nous allons utiliser est la suivante.
/* Retourne le nombre de nombres premiers dans l'intervalle [ from; to [ */
int nbPremiers( int from, int to ) {
int i, j, nb, estPremier;
nb = 0;
if( from < 2 ) from = 2;
for( i = from; i < to; i++ ) {
estPremier = 1;
for( j = 2 ; j < i ; j++ ) {
if( 0 == (i % j) ) {
estPremier = 0;
break;
}
}
nb += estPremier ;
}
return nb;
}
Le processus de rang 0 coordonne le calcul : nous allons l'appeler le processus maître. Les autres processus ne font que calculer ce que le maître leur dit de calculer. Ce sont les processus esclaves. On peut également les appeler les processus travailleurs, bien que cette dénomination, utilisée en anglais (master/worker) ne soit utilisée que rarement en français.
Écrivez un programme MPI où le processus de rang 0 appelle une fonction maitre
et les autres processus appellent une fonction esclave
.
int main( int argc, char** argv ) {
int rank;
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
if( 0 == rank ) {
maitre();
} else {
esclave();
}
MPI_Finalize();
return EXIT_SUCCESS;
}
Dans un premier temps, le processus 0 découpe l'intervalle en autant de tranches de taille égale qu'il y a de processus. Pour chaque esclave, il calcule les bornes de l'intervalle sur lequel il va travailler, et il lui envoie ces bornes. Il lui envoie donc un tableau de deux entiers. Il garde ensuite la fin de l’intervalle pour lui.
Implémentez vos fonctions maitre
et esclave
pour que :
- pour chaque processus esclave, le processus maître calcule les bornes de l'intervalle sur lequel il va travailler
- le processus maître lui envoie les bornes calculées en une seule communication
- le processus esclave reçoit ces bornes
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#define TAG 42
#define MAX 1000
void esclave(){
int intervalle[2];
int rank;
MPI_Status status;
int nb;
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Recv( intervalle, 2, MPI_INT, 0, TAG, MPI_COMM_WORLD, &status );
printf( "%d Je vais travailler de %d à %d\n", rank, intervalle[0], intervalle[1] );
}
void maitre(){
int intervalle[2];
MPI_Status status;
int size, i, nb, total;
MPI_Comm_size( MPI_COMM_WORLD, &size );
intervalle[0] = 0;
intervalle[1] = MAX / size;
for( i = 1 ; i < size ; i++ ) {
MPI_Send( intervalle, 2, MPI_INT, i, TAG, MPI_COMM_WORLD );
intervalle[0] = intervalle[1];
intervalle[1] += MAX / size;
}
printf( "%d Je vais travailler de %d à %d\n", rank, intervalle[0], MAX );
}
Chaque processus a maintenant les bornes de l'intervalle sur lequel il va travailler : appelez la fonction qui compte le nombre de nombres premiers dans cet intervalle. Une fois qu'il a le résultat, chaque processus l’envoie au processus maître, qui se charge de les additionner et d'afficher le résultat. N'oubliez pas que le processus maître a lui aussi un calcul à faire.
void esclave(){
int intervalle[2];
MPI_Status status;
int nb;
MPI_Recv( intervalle, 2, MPI_INT, 0, TAG, MPI_COMM_WORLD, &status );
nb = nbPremiers( intervalle[0], intervalle[1] );
MPI_Send( &nb, 1, MPI_INT, 0, TAG, MPI_COMM_WORLD );
}
void maitre(){
int intervalle[2];
MPI_Status status;
int size, i, nb, total;
MPI_Comm_size( MPI_COMM_WORLD, &size );
intervalle[0] = 0;
intervalle[1] = MAX / size;
for( i = 1 ; i < size ; i++ ) {
MPI_Send( intervalle, 2, MPI_INT, i, TAG, MPI_COMM_WORLD );
intervalle[0] = intervalle[1];
intervalle[1] += MAX / size;
}
nb = nbPremiers( intervalle[0], MAX );
total = nb;
for( i = 1 ; i < size ; i++ ) {
MPI_Recv( &nb, 1, MPI_INT, i, TAG, MPI_COMM_WORLD, &status );
total += nb;
}
printf( "total : %d\n", total );
}
En regardant la fonction nbPremiers
, vous pouvez constater que le temps de calcul n’est pas le même si on travail sur des petits nombres et sur des grands nombres : en effet, même si la boucle extérieure traite le même nombre d'éléments, la boucle intérieur traite un nombre d'éléments qui dépend de la valeur des bornes de l’intervalle. Ainsi, avec l'approche que nous venons de voir, les processus qui ont la fin de l’intervalle ont beaucoup plus de calcul à faire que ceux qui ont le début de l'intervalle. La charge est donc déséquilibrée entre les processus.
On peut modifier l’approche du schéma maître-esclave pour qu'il équilibre mieux la charge. Le maître découpe l'intervalle de travail en plus petits sous-intervalles, et il renvoie du travail aux esclaves dès qu'ils ont terminé. Dans ce schéma, ce sont les esclaves qui demandent du travail.
Écrivez de nouvelles fonctions maitre
et esclave
dans lesquelles :
- chaque esclave envoie une demande de travail, c'est-à-dire qu'il effectue une communication vers le maître en lui envoyant un entier quelconque avec un tag particulier. Ici, on utilisera les tag à des fins de signalisation.
- le maître reçoit ces messages et affiche l'expéditeur et le tag du message. Attention, les messages veulent arriver dans n’importe quel ordre : il faut être capable de les traiter quelque soit l'esclave qui a envoyé le message.
#define REQWORK 987
void esclave(){
int nb;
MPI_Send( &nb, 1, MPI_INT, 0, REQWORK, MPI_COMM_WORLD );
}
void maitre(){
MPI_Status status;
int size, i;
MPI_Comm_size( MPI_COMM_WORLD, &size );
for( i = 0 ; i < size - 1 ; i++ ) {
MPI_Recv( &nb, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status );
printf( "Recv tag %d from %d\n", status.MPI_TAG, status.MPI_SOURCE );
}
}
Le processus maître calcule les bornes de l’intervalle sur lequel chaque esclave va travailler. Ici, comme les calculs les plus longs sont faits en fin d'intervalle, on va parcourir l'intervalle à reculons : si chaque intervalle est de longueur SLICE
et qu'on veut aller jusqu'à MAX
, le premier intervalle envoyé sera [MAX-SLICE, MAX[
, le suivant sera [MAX-1*SLICE, MAX-SLICE[
, et ainsi de suite.
Modifiez vos fonctions maitre
et esclave
pour qu'à chaque esclave qui demande du travail, le maître envoie les bornes d'un intervalle. Cet envoi se fait avec un tag particulier. L'esclave reçoit les bornes de cet intervalle, teste la valeur du tag et, si c'est bien ce tag qu'il reçoit, appelle la fonction de comptage des nombres premiers dans cet intervalle.
#define TAGWORK 42
#define REQWORK 987
#define MAX 1000
#define SLICE 100
void esclave(){
int intervalle[2];
MPI_Status status;
int nb;
/* Demander du travail */
MPI_Send( &nb, 1, MPI_INT, 0, REQWORK, MPI_COMM_WORLD );
/* Recevoir les bornes d'un intervalle */
MPI_Recv( intervalle, 2, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status );
nb = nbPremiers( intervalle[0], intervalle[1] );
printf( "il y a %d nombres premiers entre %d et %d\n", nb, intervalle[0], intervalle[1] );
}
void maitre(){
int intervalle[2];
MPI_Status status;
int size, i, nb, total;
MPI_Comm_size( MPI_COMM_WORLD, &size );
intervalle[0] = MAX - SLICE;
intervalle[1] = MAX;
for( i = 0 ; i < size - 1 ; i++ ) {
MPI_Recv( &nb, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status );
printf( "Recv tag %d from %d\n", status.MPI_TAG, status.MPI_SOURCE );
MPI_Send( intervalle, 2, MPI_INT, status.MPI_SOURCE, TAGWORK, MPI_COMM_WORLD );
intervalle[1] = intervalle[0];
intervalle[0] -= SLICE;
}
}
Les esclaves ont fait un calcul : ils envoient le résultat au maître, qui additionne tous les résultats qu'il a reçus. Ajoutez à vos fonctions maitre
et esclave
l'envoi du résultat. Ici, c'est une communication différente de la communication initiale où chaque esclave demande du travail : la donnée transmise va être utilisée, donc ce message ne doit pas être traité de la même façon. Utiliser donc un tag différent, et le maître va distinguer ces messages en utilisant leur tag.
#define TAGWORK 42
#define REQWORK 987
#define RESULT 2345
#define MAX 1000
#define SLICE 100
void esclave(){
int intervalle[2];
MPI_Status status;
int nb;
/* Demander du travail */
MPI_Send( &nb, 1, MPI_INT, 0, REQWORK, MPI_COMM_WORLD );
/* Recevoir les bornes d'un intervalle */
MPI_Recv( intervalle, 2, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status );
nb = nbPremiers( intervalle[0], intervalle[1] );
printf( "il y a %d nombres premiers entre %d et %d\n", nb, intervalle[0], intervalle[1] );
/* Envoyer le résultat au maître */
MPI_Send( &nb, 1, MPI_INT, 0, RESULT, MPI_COMM_WORLD );
}
void maitre(){
int intervalle[2];
MPI_Status status;
int size, i, nb, total;
MPI_Comm_size( MPI_COMM_WORLD, &size );
total = 0;
intervalle[0] = MAX - SLICE;
intervalle[1] = MAX;
for( i = 0 ; i < size - 1 ; i++ ) {
MPI_Recv( &nb, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status );
MPI_Send( intervalle, 2, MPI_INT, status.MPI_SOURCE, TAGWORK, MPI_COMM_WORLD );
intervalle[1] = intervalle[0];
intervalle[0] -= SLICE;
}
for( i = 0 ; i < size - 1 ; i++ ) {
MPI_Recv( &nb, 1, MPI_INT, MPI_ANY_SOURCE, RESULT, MPI_COMM_WORLD, &status );
total += nb;
}
}
Pour le moment, le maître a envoyé du travail exactement une fois à chaque esclave. Modifiez vos deux fonctions pour que le maître envoie du travail tant que l'intervalle n'est pas entièrement parcouru. Une fois qu'il a tout envoyé, il renvoie un message d'un autre type aux esclaves afin de leur signifier la fin du travail. Vous utiliserez un autre tag pour cela.
Quand l'esclave reçoit quelque chose du maître, il teste si il s'agit d'un signal de fin. Si c'est le cas, il termine la fonction. Sinon, il traite le calcul et il envoie le résultat.
Le maître reçoit des demandes de travail : si il a encore des intervalles à attribuer, il le fait. Sinon, il récupère les derniers résultats et il envoie le signal de fin aux esclaves.
#define TAGWORK 42
#define TAGFINI 1664
#define REQWORK 987
#define RESULT 2345
#define MAX 1000
#define SLICE 100
int nbPremiers( int, int );
void esclave(){
int intervalle[2];
MPI_Status status;
int nb;
/* Demander du travail */
MPI_Send( &nb, 1, MPI_INT, 0, REQWORK, MPI_COMM_WORLD );
while( 1 ) {
/* Recevoir les bornes d'un intervalle */
MPI_Recv( intervalle, 2, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status );
if( status.MPI_TAG == TAGFINI ) { /* plus de travail, on a terminé */
return;
}
/* On a reçu du travail, on le calcule et on envoie le résultat */
nb = nbPremiers( intervalle[0], intervalle[1] );
MPI_Send( &nb, 1, MPI_INT, 0, RESULT, MPI_COMM_WORLD );
}
}
void maitre(){
int intervalle[2];
MPI_Status status;
int size, i, nb, total;
MPI_Comm_size( MPI_COMM_WORLD, &size );
total = 0;
intervalle[0] = MAX - SLICE;
intervalle[1] = MAX;
while( intervalle[0] >= 0 ) {
MPI_Recv( &nb, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status );
if( status.MPI_TAG == RESULT ) { /* On a reçu un résultat avec le message */
total += nb;
}
MPI_Send( intervalle, 2, MPI_INT, status.MPI_SOURCE, TAGWORK, MPI_COMM_WORLD );
intervalle[1] = intervalle[0];
intervalle[0] -= SLICE;
}
/* On a tout envoyé : on reçoit les derniers résultats et on dit que c'est fini */
for( i = 0 ; i < size - 1 ; i++ ) {
MPI_Recv( &nb, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status );
if( status.MPI_TAG == RESULT ) {
total += nb;
}
MPI_Send( intervalle, 2, MPI_INT, status.MPI_SOURCE, TAGFINI, MPI_COMM_WORLD );
}
printf( "total : %d\n", total );
}