Rapport sur la simulation de jet de gouttes d'eau en MPI - EFREI I2
Rougier Dorian - rougier at efrei dot fr
Michel Sebastien - michels at efrei dot fr
Sommaire
Tout le monde à déjà du essayer de diriger un jet d'eau vers le haut et à regarder comment les gouttes tombées au sol.
Ce projet, c'est un peu cela, nous avons des gouttes au départ qui partent à peu près toutes du même endroit et on veut savoir où elles vont tomber.
Mais comme ceci nécessite beaucoup de calcul, pour gagner du temps nous allons faire le programme pour qu'il fonctionne sur plusieurs processeurs, donc nous allons faire de la programmation parallèle en C grâce aux fonction MPI.
Voici donc notre programme et les résultats trouvés.
#include
<stdio.h>
#include <stdlib.h>
#include
"mpi.h"
#include <math.h>
#define
FILE_INIT "data_initiale_1000"
#define NB_GOUTTE 1000
#define
rayon 0.001
#define false 0
#define true 1
typedef struct
goutte{
float x,y,z;
//position réel de la goutte en metre
float vx,vy,vz;
//vitesse initiale de la goutte en metre/s
float masse; //en
microgramme
int numero; //indispensable pour l'affichage
} Goutte;
int main(int argc,char
*argv[])
{
int
tasks,i;//,j;
FILE
*fic,*ficf;
int nb_goutte=NB_GOUTTE; // 1000
int
nb_goutte_presente,nb_goutte_presente_avant,nb_goutte_fin,nb_goutte_presente_totale;
int
fus=0,fus_tot=0,fus_tot1,sol=0,sol_tot=0,sol_tot1;
float t=0;
// le temps est en seconde
float
acc=-9.81; // que sur les z
// float rayon=0.001; // .002 rayon d'attraction des gouttes entre elles
Goutte
temp_g,g[nb_goutte],ge0[nb_goutte],ge1[nb_goutte],gr0[nb_goutte],gr1[nb_goutte];
int
tab_num_goutte[nb_goutte],tab_num_goutte2[nb_goutte];
int ig=0,jg=0;
// les unités sont en metre
float
maxx=.1,minx=-maxx;
float
double dt=.01; //float //0.01 le temps
s'incrémente par 1/100 de seconde
int
nb_envoie0,nb_recu0;
int
nb_envoie1,nb_recu1;
double
MPI_Wtime();
MPI_Datatype
types[8]={MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_INT};
int
longueur_blocs[8]={1,1,1,1,1,1,1,1};
double
temps_debut,temps_fin;
int me,right_proc,left_proc;
MPI_Status status;
MPI_Aint adresse[8];
MPI_Aint deplacement[8];
MPI_Datatype MPI_Goutte;
// printf("debut de tous le
porgramme");
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&me);
MPI_Comm_size(MPI_COMM_WORLD,&tasks);
MPI_Address(&temp_g.x,&adresse[0]);
MPI_Address(&temp_g.y,&adresse[1]);
MPI_Address(&temp_g.z,&adresse[2]);
MPI_Address(&temp_g.vx,&adresse[3]);
MPI_Address(&temp_g.vy,&adresse[4]);
MPI_Address(&temp_g.vz,&adresse[5]);
MPI_Address(&temp_g.masse,&adresse[6]);
MPI_Address(&temp_g.numero,&adresse[7]);
for
(i=0;i<=7;i++) deplacement[i]=adresse[i]-adresse[0];
MPI_Type_struct(8,longueur_blocs,deplacement,types,&MPI_Goutte);
MPI_Type_commit(&MPI_Goutte);
temps_debut=MPI_Wtime();
if (me<tasks-1) right_proc=me+1;
else right_proc=MPI_PROC_NULL;
if (me==0) left_proc=MPI_PROC_NULL;
else left_proc=me-1;
// printf("rang
:\"%i\"\t\"%i\"\t\"%i\"\n",left_proc,me,right_proc);
//
// temps_debut=MPI_Wtime();
fic=fopen(FILE_INIT,"r");
fread(&nb_goutte,sizeof(nb_goutte),1,fic);
// printf("il y a %i valeur
\n",nb_goutte);
for
(ig=0;ig<nb_goutte;ig++)
{
fread(&temp_g,sizeof(temp_g),1,fic);
if
( (temp_g.x>=( (maxx-minx) / tasks* me
+minx )) &&
(temp_g.x<( (maxx-minx) /
tasks*(me+1)+minx )))
{
g[nb_goutte_presente]=temp_g;
nb_goutte_presente++;
}
}
//
compteur qui nous servira au stockage des gouttes fusionné ou au sol en fin de
tableau
// Car a la suite des gouttes_presentes nous allons rajouter des
gouttes venant d'autre proc
nb_goutte_fin=nb_goutte;
/*
//
Verif de la distribution des gouttes entre les proc !
if (me==0)
{
for (i=0;i<nb_goutte_presente;i++)
//printf("Goutte : %f %f %f %f %f %f
%f %i\n",g[i].x,g[i].y,g[i].z,g[i].vx,g[i].vy,g[i].vz,g[i].masse,g[i].numero);
}
printf("task
%i\n",tasks);
/****************
on rentre ds la boucle de calcul **********************/
nb_goutte_presente_totale=nb_goutte;
while
(nb_goutte_presente_totale!=0)
{ // boucle sur le temps
t=t+dt;
fus=0; // nbre de gouttes qui fussionnent
sol=0; // nbre de goutte qui tombent sur
le sol
nb_envoie0=0; //nbre de
goutte a envoie
nb_envoie1=0;
//nbre de goutte a envoie
nb_goutte_presente_avant=nb_goutte_presente;
// déplacement de goutte (la vitesse ne
change pas sauf sur les z)
for
(ig=0;ig<nb_goutte_presente;ig++)
//calcul de toute les nouvelles
positions
{
temp_g=g[ig];
g[ig].x =g[ig].x
+g[ig].vx*dt ;
g[ig].y =g[ig].y +g[ig].vy*dt ;
g[ig].z
=g[ig].z +g[ig].vz*dt + acc * (pow (dt,2)) /2;
//la fct pow ne
fonctionnement pas !!!??? ....
finalement il fallait rajouter -lm en arg de hcc :)
// g[ig].z
=g[ig].z +g[ig].vz*dt + acc * (dt*dt) /2;
g[ig].vz=g[ig].vz+acc*dt;
//vx
et vy n'évoluent pas, elles restent constantes !
//si la goutte retombe
sur le sol
if (g[ig].z<0)
{
//calcul
de l'intersection avec le sol
g[ig].x=((-g[ig].x*temp_g.z+g[ig].z*temp_g.x)/(g[ig].x-temp_g.x))*((g[ig].x-temp_g.x)/(g[ig].z-temp_g.z));
g[ig].y=((-g[ig].y*temp_g.z+g[ig].z*temp_g.y)/(g[ig].y-temp_g.y))*((g[ig].y-temp_g.y)/(g[ig].z-temp_g.z));
g[ig].z=0;
//déplacement
de la particule en tête des particules déjà traitées
//
temp_g=g[ig];
// g[ig]=g[nb_goutte_presente-1];
//
g[nb_goutte_presente-1]=temp_g;
temp_g=g[nb_goutte_presente-1]; //
sauvegarde du g[nb_goutte_presente-1] pout ne pas
//
le perdre en cas d'ecrasement de la part de g[ig]
nb_goutte_fin--;
g[nb_goutte_fin]=g[ig];
g[ig]=temp_g;
nb_goutte_presente--;
ig--;
sol++;
}
//stockage des gouttes a envoyer sur le proc de gauche
else
if ((me !=0) && g[ig].x<( (maxx-minx) / tasks* me +minx ))
{
// temp_g=g[ig];
g[ig]=g[nb_goutte_presente-1];
//
g[nb_goutte_presente-1]=temp_g;
g[nb_goutte_presente-1].masse=-t; // enlever pour optimiser !
ge0[nb_envoie0]=temp_g;
nb_envoie0++;
nb_goutte_presente--;
ig--;
}
//stockage
des gouttes a envoyer sur le proc de droite
else if ((me
!=tasks-1)&& g[ig].x>=( (maxx-minx) / tasks*(me+1)+minx ))
{
//
temp_g=g[ig];
g[ig]=g[nb_goutte_presente-1];
// g[nb_goutte_presente-1]=temp_g;
g[nb_goutte_presente-1].masse=-t; // enlever pour optimiser !
ge1[nb_envoie1]=temp_g;
nb_envoie1++;
nb_goutte_presente--;
ig--;
}
}
//**** envoie non bloquant
reception bloquant **
//********
passage des gouttes entre les proc
***************
//envoie des gouttes changants de zones .
//
1- envoie du nombre de goutte
// 2- envoie des gouttes.
nb_recu0=nb_recu1=0;
//traitement du proc de gauche
if (me!=0)
{
MPI_Sendrecv(&nb_envoie0,1,MPI_INT,left_proc,100,&nb_recu0,1,MPI_INT,left_proc,100,MPI_COMM_WORLD,&status);
//if (me==0) nb_recu0=0;
MPI_Sendrecv(&ge0,nb_envoie0,MPI_Goutte,left_proc,100,&gr0,nb_recu0,MPI_Goutte,left_proc,100,MPI_COMM_WORLD,&status);
}
//traitement du proc de
droite
if (me!=tasks-1)
{
MPI_Sendrecv(&nb_envoie1,1,MPI_INT,right_proc,100,&nb_recu1,1,MPI_INT,right_proc,100,MPI_COMM_WORLD,&status);
MPI_Sendrecv(&ge1,nb_envoie1,MPI_Goutte,right_proc,100,&gr1,nb_recu1,MPI_Goutte,right_proc,100,MPI_COMM_WORLD,&status);
}
//placement
des gouttes nouvellement arrivé ds le tableau
//traitement des gouttes
arrive de gauche
for(i=0;i<nb_recu0;i++)
{
g[nb_goutte_presente]=gr0[i];
nb_goutte_presente++;
}
//traitement
des gouttes arrive de droite
for(i=0;i<nb_recu1;i++)
{
g[nb_goutte_presente]=gr1[i];
nb_goutte_presente++;
}
//
les gouttes fusionnent t-elles ensemble ? (analyse deux à deux)
nb_envoie1=0;
for (ig=0;ig<nb_goutte_presente;ig++)
{
for (jg=ig+1;jg<nb_goutte_presente;jg++)
{
if
((fabs(g[ig].x-g[jg].x)<rayon)&&(fabs(g[ig].y-g[jg].y)<rayon)&&(fabs(g[ig].z-g[jg].z)<rayon))
{
fus++;
g[ig].vx=(g[ig].masse*g[ig].vx+g[jg].masse*g[jg].vx)/(g[ig].masse+g[jg].masse);
g[ig].vy=(g[ig].masse*g[ig].vy+g[jg].masse*g[jg].vy)/(g[ig].masse+g[jg].masse);
g[ig].vz=(g[ig].masse*g[ig].vz+g[jg].masse*g[jg].vz)/(g[ig].masse+g[jg].masse);
g[ig].x=(g[ig].x+g[jg].x)/2;
g[ig].y=(g[ig].y+g[jg].y)/2;
g[ig].z=(g[ig].z+g[jg].z)/2;
g[ig].masse=g[ig].masse+g[jg].masse;
g[jg].masse=-t; //pour savoir qd une goutte a été fusionnée
temp_g=g[jg];
g[jg]=g[nb_goutte_presente-1];
g[nb_goutte_presente-1]=temp_g;
nb_goutte_presente--;
jg--;
}
}// inserer le stockage des gouttes a
envoyer sur le proc droite pour optimiser !
// pas de changement pour les resultats
if ((me != tasks-1)&& g[ig].x>=( (maxx-minx) /
tasks*(me+1) + minx - rayon ))
{
ge1[nb_envoie1]=g[ig];
nb_envoie1++;
}
}
//**** envoie non bloquant reception bloquant **
//******** passage des gouttes entre les proc ***************
//envoie
des gouttes changants de zones .
//
1- envoie du nombre de goutte
//
2- envoie des gouttes
//traitement
du proc de droite
MPI_Sendrecv(&nb_envoie1,1,MPI_INT,right_proc,100,&nb_recu1,1,MPI_INT,left_proc,100,MPI_COMM_WORLD,&status);
if (me==0) nb_recu1=0;
MPI_Sendrecv(&ge1,nb_envoie1,MPI_Goutte,right_proc,100,&gr1,nb_recu1,MPI_Goutte,left_proc,100,MPI_COMM_WORLD,&status);
// printf("casse
tete rang %i recu %i envoie
%i\n",me,nb_recu1,nb_envoie1);
// verif des fusions entre les gouttes des
frontieres
nb_envoie0=0;
for (ig=0;ig<nb_recu1;ig++)
{
for
(jg=0;jg<nb_goutte_presente;jg++)
{
if
((fabs(gr1[ig].x-g[jg].x)<rayon)&&(fabs(gr1[ig].y-g[jg].y)<rayon)&&(fabs(gr1[ig].z-g[jg].z)<rayon))
{
fus++;
g[jg].vx=(gr1[ig].masse*gr1[ig].vx+g[jg].masse*g[jg].vx)/(gr1[ig].masse+g[jg].masse);
g[jg].vy=(gr1[ig].masse*gr1[ig].vy+g[jg].masse*g[jg].vy)/(gr1[ig].masse+g[jg].masse);
g[jg].vz=(gr1[ig].masse*gr1[ig].vz+g[jg].masse*g[jg].vz)/(gr1[ig].masse+g[jg].masse);
g[jg].x=(gr1[ig].x+g[jg].x)/2;
g[jg].y=(gr1[ig].y+g[jg].y)/2;
g[jg].z=(gr1[ig].z+g[jg].z)/2;
g[jg].masse=gr1[ig].masse+g[jg].masse;
tab_num_goutte[nb_envoie0]=gr1[ig].numero;
nb_envoie0++;
break; // pour ne pas retester la goutte
deja fusionne avec les autres gouttes
}
}
}
// a la reception des gouttes qui ont ete
fusionnees
MPI_Sendrecv(&nb_envoie0,1,MPI_INT,left_proc,100,&nb_recu0,1,MPI_INT,right_proc,100,MPI_COMM_WORLD,&status);
if (me ==tasks-1) nb_recu0=0;
MPI_Sendrecv(&tab_num_goutte,nb_envoie0,MPI_INT,left_proc,100,&tab_num_goutte2,nb_recu0,MPI_INT,right_proc,100,MPI_COMM_WORLD,&status);
for (ig=0;ig<nb_recu0;ig++)
{
for
(jg=0;jg<nb_goutte_presente;jg++)
{
if (tab_num_goutte2[ig]==g[jg].numero)
{
g[jg]=g[nb_goutte_presente-1];
g[nb_goutte_presente-1].masse=-t;
nb_goutte_presente--;
break;
}
}
}
fus_tot=fus_tot+fus;
sol_tot=sol_tot+sol;
// printf("il y a %i gouttes qui ont
fusionné et %i qui sont tombées au sol\n",fus,sol);
//sauvegarde des data
// calcul du nb de goutte à
sauvegarder
/* //
Large possibilite d'optimisation !
nb_a_sauver=0;
for
(ig=nb_goutte_presente;ig<nb_goutte_presente_avant;ig++)
{
if
(g[ig].masse>0) nb_a_sauver++ ;
}
fwrite(&nb_a_sauver,sizeof(nb_a_sauver),1,fic);
for
(ig=nb_goutte_presente;ig<nb_goutte_presente_avant;ig++)
{
if
(g[ig].masse>0)
{
fwrite(&g[ig],sizeof(g[ig]),1,fic);
}
}
*/
MPI_Allreduce(&nb_goutte_presente,&nb_goutte_presente_totale,1,MPI_INT,MPI_SUM,MPI_COMM_WORLD);
}
//printf("nombre
fus %i\n",fus_tot);
// ouverture du fichier finale
//if (me==0)
//unlink("data_finale_mpi");
MPI_Reduce(&sol_tot,&sol_tot1,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);//mise
en commun des donnees finales
for
(i=0;i<tasks;i++)
{
if
(me==0 && i==0)
{
ficf=fopen("data_finale_mpi","w"); //pour etre sur que le programme soit
bon
fwrite(&sol_tot1,sizeof(sol_tot1),1,ficf);
for(ig=nb_goutte-1;ig>=nb_goutte_fin;ig--)
{
//printf("rank
: %i => %i gouttes\n",me,sol_tot);
fwrite(&g[ig],sizeof(g[ig]),1,ficf);
}
fclose(ficf);
}
else
if (me==i && i!=0)
{
ficf=fopen("data_finale_mpi","a");
for(ig=nb_goutte-1;ig>=nb_goutte_fin;ig--)
{
//printf("rank : %i => %i
gouttes\n",me,sol_tot);
fwrite(&g[ig],sizeof(g[ig]),1,ficf);
}
fclose(ficf);
}
MPI_Barrier(MPI_COMM_WORLD);
}
MPI_Reduce(&fus_tot,&fus_tot1,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);
temps_fin=MPI_Wtime();
if(me==0)
printf("-->il y %i gouttes qui ont
fusionné et %i qui sont tombées au sol\n",fus_tot1,sol_tot1);
fclose(fic);
//printf("--->la similation a durée %f
secondes\n",t);
printf("Tout les proc :
%f\n",temps_fin-temps_debut);
printf("Goutte
de %i : %i\n",me,nb_goutte_presente);
MPI_Type_free(&MPI_Goutte);
MPI_Finalize();
return 0;
}
le principe est assez simple:
Au debut, les gouttes sont aléatoirement disposées sur un carré de 0.2 de coté.
Elles ont une vitesse et une direction aléatoire mais choisi dans un interval de tel manière à ce que les gouttes ne sortent jamais d'une certaine zone (un carré de 0.4 de coté).
Le but pour nous est de paralleliser le calcul de trajectoire de ces gouttes
|
|
Après quelques essais de l'execution du code sequentiel permettant de calculer la trajectoire des gouttes, nous avons décidé de couper la zone des gouttes en "n" bandes correspondants aux "n" processeurs disponibles pour le calcul.
Nous avons determiné la largeur des bandes en fonction de de la zone de départ des gouttes (le petit carré de 0.2 de coté).
Aussi nous pensons avoir la topologie la plus simple possible avec toutefois un très bon équilibre des charges de travail des processeurs (cf expliquations ci dessous)
|
|
Dans la mesure où, le nombre de goutte sortant d'un seul coté de la zone du petit carré est de seulement 7% à 9%, les processeurs des extremités ne traiteront que 8 à 10% de goutte en plus par rapport aux processeurs du centre.
|
|
En ce qui concerne la communication interprocesseur, elle se passe en 3 étapes:
- La première étape est le transfert des gouttes qui sont sorties de la zone du processeur ayant calculé leur déplacement.
Pour cette étape, chaques processeurs stockent dans deux tableaux les gouttes qui sont sorties à droite de la zone et celles qui sont sorties à gauche.
Ensuite ils les envoient à leurs voisins en des etapes:
- Envois du nombre de goutte à transferer et reception du nombre de goutte à recevoir.
- Envois des gouttes par le biais d'un tableau et reception des nouvelles gouttes dans ce meme tableau.
- La deuxieme étape, est l'échange des gouttes suceptibles de de fusionner avec des gouttes qui sont sur d'autres processeurs (c'est à dire une bande de frontière entre 2 proc.).
Cette étape est unilatérale. C'est à dire que seul le proc. de gauche envois les gouttes au proc. de droite.
Ensuite le proc. font une verification de fusion entre leurs gouttes et les gouttes nouvellement envoyées. Pendant cette fusion, le proc. stock l'ID de chaque goutte nouvellement envoyées qui a fusionné avec une de ces gouttes.
- Enfin, la troisieme etape de communication se fait juste apres la deuxieme. Elle consiste à envoyer le tableau des ID de goutte ayant fusionnées avec les gouttes des proc. voisins.
Voici le tableau récapitulatif des performances du programme:
| | 1 p: | 2 p: | 3 p: | 4 p: | 5 p: |
| rayon=,001 | 10000 Gouttes | 416 | 111,159 | 51,983 | 31,557 | 21,194025 |
| rayon=,001 | 1000 Gouttes | 4,567028 | 1,472331 | 1,098544 | 1,023605 | 1,165001 |
| rayon=,001 | 100 Gouttes | 0,096341 | 0,284744 | 0,445053 | 0,579858 | 0,688806 |
| rayon=,01 | 10000 Gouttes | 1,040138 | 0,666881 | 0,672115 | 0,785330 | 0,886456 |
| rayon=,01 | 1000 Gouttes | 0,429747 | 0,450498 | 0,574991 | 0,665447 | 0,799617 |
| rayon=,01 | 100 Gouttes | 0,081315 | 0,294300 | 0,499458 | 0,600920 | 0,719120 |
A partir de ce tableau, nous avons tracé deux graphes qui nous paraissent significatifs:
Ce premier graphique montre l'avantage du code parallele par rapport au code sequentiel:
La courbe rose correspond au nombre de seconde que le code sequentiel prend pour executer le programme avec 10000 gouttes avec un rayon de fusion de 0.001, Alors que la courbe bleue correspond aux perf. du code parallele.
Nous pouvons clairement voir que l'avantage tourne tres vite et largement en faveur du code parallele.
Notons que nous en sommes pas allez plus loin en nombre de proc. et que le programme doit s'écrouler au niveau de ces performance(cf tableau d'apres) a un moment ou un autre que l'on essayera de determiner ci-dessous.
Ce second graphique montre les limites du code parallele.
Dans notre cas, nous executons le programme eu seins d'un réseau 100Mb/s de sprac Ultra5.
Le probleme du code parallele dans ce genre de configuration, c'est la lenteur de communication entre les procs car il faut passer par le LAN.
Dans ce cas, nous voyons clairement que l'execution parallele n'a aucun interet pour 100 Gouttes comme pour 1000 Gouttes dans la mesure ou le temps d'execution augement des qu'il y a deux procs au lieu d'un. Quant a l'execution avec 10000 Gouttes elle n'est rentable qu'avec 2 procs!...
En tout cas, nous voyons clairement que les courbe tendent toutes vers une meme asymptote.
Grace a ces informations, nous pouvons interpreter les résultats afin d'interpoler l'écroulement des performences dans l'execution precedente:
Je pense que les performance commence a baisser vers 6 procs pour etre encore plus lent que le code seqentiel vers 11 ou 12 procs. Car la courbe tendrait vers une asymptote linéraire qui est a 0 seconde pour 7 ou 8 procs et 450 secondes pour environ 15 procs.
Voici le nombre de gouttes qui ont fusionnées:
| | 1 p: | 2 p: | 3 p: | 4 p: | 5 p: |
| rayon=,001 | 10000 Gouttes | 976 | 999 | 1011 | 1047 | 1012 |
| rayon=,001 | 1000 Gouttes | 11 | 11 | 9 | 10 | 10 |
| rayon=,001 | 100 Gouttes | 0 | 0 | 0 | 0 | 0 |
| | 1 p: | 2 p: | 3 p: | 4 p: | 5 p: |
| rayon=,01 | 10000 Gouttes | 9709 | 9710 | 9728 | 9723 | 9715 |
| rayon=,01 | 1000 Gouttes | 780 | 777 | 787 | 782 | 791 |
| rayon=,01 | 100 Gouttes | 30 | 31 | 30 | 31 | 31 |
| 10000 gouttes | R=0,001 |
| 5P | 4P | 3P | 2p |
| nombre de gouttes | 18534 | 12994 | 7165 | 1625 |
| nbre gouttes ss fusion | 6201 | 4641 | 3185 | 1625 |
| nbre de com | 4386 | 3204 | 1900 | 596 |
|
| 10000 gouttes | R=0,01 |
| 5P | 4P | 3P | 2p |
| nombre de gouttes | 3704 | 2581 | 1142 | 65 |
| nbre gouttes ss fusion | 162 | 157 | 102 | 65 |
| nbre de com | 2060 | 1385 | 867 | 433 |
|
| 1000 gouttes | R=0,001 |
| 5P | 4P | 3P | 2p |
| nombre de gouttes | 2110 | 1448 | 814 | 184 |
| nbre gouttes ss fusion | 733 | 541 | 386 | 184 |
| nbre de com | 3634 | 2560 | 1631 | 668 |
|
| 1000 gouttes | R=0,01 |
| 5P | 4P | 3P | 2p |
| nombre de gouttes | 3010 | 1734 | 1001 | 28 |
| nbre gouttes ss fusion | 98 | 68 | 43 | 28 |
| nbre de com | 2060 | 1489 | 964 | 430 |
|
| 100 gouttes | R=0,001 |
| 5P | 4P | 3P | 2p |
| nombre de gouttes | 171 | 147 | 83 | 19 |
| nbre gouttes ss fusion | 50 | 41 | 31 | 19 |
| nbre de com | 1778 | 1364 | 788 | 425 |
|
| 100 gouttes | R=0,01 |
| 5P | 4P | 3P | 2p |
| nombre de gouttes | 1022 | 599 | 344 | 14 |
| nbre gouttes ss fusion | 39 | 19 | 21 | 14 |
| nbre de com | 1959 | 1452 | 931 | 422 |
|
Evaluation des ressources mémoire.
Il y différent type de variable dans le programme.
En premier nous allons voir les variable de type entier :
| nb_goutte | qui représente le nombre totale de goutte que l'on étudie. |
| nb_goutte_presente | nombre de goutte présente dans un processeur. |
| nb_goutte_presente_avant | nombre de goutte présente dans le processeur avant de recommencer la boucle de traitement, pour savoir combien de boucle on disparue après chaque boucle. |
| nb_goutte_fin | nous mettons les gouttes a la fin du tableau, lorsqu'elles sont tombées au sol, donc ce compteur permet de savoir combien de gouttes sont tombées qu sol, et où elles sont dans le tableau. |
| nb_goutte_presente_totale | c'est un compteur pour savoir combien il reste de gouttes présentent en tout dans tous les processeurs. |
| fus | compte le nombre de fusion pour une boucle. |
| fus_tot | compte le nombre de fusion total pour un processeur. |
| sol | compte le nombre de goutte qui sont tombées au sol pour un processeur |
| sol_tot | compte le nombre total de gouttes qui sont tombées par terre pour un processeur |
| sol_tot1 | nombre total pour tous les processeurs qui sont tombées par terre. |
| i ig et ij | compteurs pour les for |
| nb_envoie0 et nb_envoie1 | variables servant de compteur pour des send pour dire combien d'objet on va envoyer |
| nb_recu0 et nb_recu1 | variables servant pour la réception du nombre d'objet a recevoir au prochain receive. |
| me | variable qui sert à garder le numéro du processeur. |
| tasks | sert à savoir combien il y a de processus dans la topologie MPI |
| right_proc, left_proc | définit le processeur de droite et de gauche pour un processeur. |
Un type particulier de int : les tableaux d'entier
| Tab_num_goutte[nb_goutte] | permet de stocker le numéro des identificateurs des gouttes qui ont fusionnées et que le processeur de gauche doit supprimer |
| Tab_num_goutte2[nb_goutte] | permet de recevoir les identificateurs des gouttes qui ont fusionné et que l'on doit supprimer. |
| longueur_blocs[8] | Tableau qui sert pour la création d'un type MPI |
Nous allons maintenant voir un autre type de variable qui sont les floats :
| T | permet de savoir le temps réel de la simulation dans la vie réel. |
| Acc | cette variable est une constante d'accélération qui sert dans les calculs. |
| Maxx, minx | sont des variables qui servent à la répartition de gouttes entre les différents processeurs. |
Un autre type qui se rapproche assez du type float : le type double :
temps_debut,temps_fin ces 2 variables servent pour savoir combien de temps la simulation a duré sur chaque processeur.
Pour ce projet nous avons du créer une structure goutte qui contient les informations d'une goutte, il y a la position de la goutte dans l'espace 3D, ainsi que les variables contenant la valeur des vitesses des 3 directions de l'espace.
Ensuite il y a la masse de la goutte, et le numéro d'identificateur.
typedef struct goutte{
float x,y,z; //position réel de la goutte en mètre
float vx,vy,vz; //vitesse initiale de la goutte en mètre/s
float masse; //en micro gramme
int numero; //indispensable pour l'affichage
} Goutte;
Ensuite nous avons créer des variables à partir de ce type :
| temp_g | c'est pour stoker une goutte lors de modification des tableaux de goutte. |
| g[nb_goutte] | contient toutes les gouttes du processeur. |
| ge0[nb_goutte], ge1[nb_goutte] | sert de tableaux de stockage des gouttes qui vont partir sur un autre processeur. |
| gr0[nb_goutte], gr1[nb_goutte] | sert de tableaux pour réceptionner les gouttes qui viennent d'un autre processeur. |
Nous avons besoin d'ouvrir et d'écrire dans des fichiers pour voir les gouttes à utiliser pour que ce soit toujours les mêmes, et pour sauvegarder les gouttes qui tombent.
| fic | fichier où l'on prend les gouttes. |
| ficf | fichier où l'on écrit toutes les gouttes qui sont tombées sur le sol, à la fin du programme. |
Pour finir nous allons voir des qui sont particuliers à MPI :
| MPI_Datatype types[8] | c'est un tableau qui contient des types de variables MPI ex :MPI_FLOAT, MPI_INT….
|
| MPI_Status status | sert pour les sendrecv |
| MPI_Aint adresse[8] | tableau contenant les adresses de variables. |
| MPI_Aint deplacement[8] | tableau contenant l'espace qui sépare des variables |
| MPI_Datatype MPI_Goutte | c'est une varaible qui sert en faite de définition pour le type goutte pour le formaliser en MPI pour pourvoir l'envoyer aux autres processeurs. |
Suivant le nombre de processeur, nous ne trouvons pas exactement le même nombre de gouttes fusionnées, en faite c'est du au faite que lorsque l'on fusionne une goutte, on ne vérifie le résultat de cette fusion avec les gouttes que l'on a regardé avant, mais lors de la fusion, les gouttes ont une nouvelle position.
Donc pour améliorer le programme il faudrait à chaque nouvelle fusion recommencer de vérifier tout le tableau avec cette fusion.
Pour être le plus prêt possible du programme monoprocesseur, nous n'avons pas inclus cette modification dans le programme multiprocesseur.
Pour finir, l'amelioration que l'on peux faire sur le monoprocesseur et qui fait que notre programme MPI marche bien, c'est que en monoprocesseur, toutes les gouttes sont verifiées deux à deux alors que sur le programme MPI, les gouttes sont deja triées par processeur, il y a donc beaucoup moins de comparaisons.
Il faudrait donc implemente un systeme de trie des gouttes sur une dimension afin de minimiser le nombre de comparaison.
Voilà, notre premier projet en programmation parallèle est finis.
Nous avons eu beaucoup de problèmes lors des compilations de notre programme.
Il faisait souvent des erreurs pendant l'exécution du programme, pour des raisons mysterieuses (trop de variable, un pointeur, un passage par adresse ....).
Au départ nous avons cru que c'était du à la topologie cartésienne, et que les machines de l'école ne le supportaient pas, mais par la suite il nous a refait l'erreur sur des send et des receive.
Finalement le problème venait de la déclaration de variable MPI.
Après tout c'est bien passé, le programme a vite était finis.
Donc à part le problème de déclaration de variable MPI tout c'est bien passé, mais il nous a fallut beaucoup de temps, avant de comprendre l'erreur (merci monsieur Proux :) ).