Appendix B Autres librairies
B.1 PVM3.4
La version 3.4 est la toute dernière version de PVM. Elle ajoute
quelques fonctionnalités type MPI, et en particulier:
- Contextes de communication
- Handlers
- Messages persistants
- etc.
B.1.1 Contextes de Communication
C'est un ``tag'' supplémentaire qui permet en particulier
de bien filtrer les messages utilisateurs des messages systèmes
(ou entre groupes, ou entre applications différentes).
Pour ces nouveaux ``tag'', il n'y a pas de possibilité de ``wildcard'' (filtrage
à la réception avec TAG=-1).
Ces ``tags'' assurent un certain nombre de propriétés. Toute création d'un nouveau
contexte
définit un unique tag pour toute une machine PVM. De plus
les tâches filles héritent
du contexte du père.
A noter que cette extension de PVM ne rend pas obsolète les anciens
programmes PVM. Les principales commandes associées sont:
- new_context=pvm_newcontext() crée un nouveau contexte
pour le reste du code,
- old_context=pvm_setcontext(new_context) change le contexte
à newcontext et renvoie l'ancien contexte,
- info=pvm_freecontext(context) libère le tag context,
- context=pvm_getcontext() renvoie le contexte courant.
Dans la pratique,
une tâche demande un nouveau contexte et le distribue à toutes
les autres taches par newcontext=pvm_newcontext().
Puis les autres tâches se mettent au nouveau contexte pendant les
appels aux fonctionnalités privées de la première tâche par:
oldcontext=pvm_setcontext(newcontext);
/* communications sures */
newcontext=pvm_setcontext(oldcontext);
pvm_freecontext(newcontext);
/* on revient a l'ancien contexte */
B.1.2 Handlers
Les ``handlers''
permettent d'exécuter une fonction C à la réception d'un message
spécifique.
Ils agissent comme des interruptions (qui n'interrompent le cours
du programme que uniquement pendant
les appels à la librairie PVM).
La fonction C (il n'y a aucune restriction d'usage) est appelée
quand un message correspond à un pattern (context,msgtag,source) donné.
Les principales commandes correspondantes sont:
- mhid=pvm_addmhf(src,tag,context,func) déclare le handler
func (n'importe quelle fonction C) à activer à la réception
de tout message compatible avec le pattern src,tag,context.
- pvm_delmhf(mhid) détruit le handler mhid.
B.1.3 Messages persistants
Les messages persistants sont des messages ``uni-directionnels'',
similaire au ``tuple-space'' de Linda.
Les tâches PVM peuvent enregister et récupérer des messages par leurs
noms: cela
organise de fait une base de données distribuée pour les processus.
Il est également possible de ranger plusieurs messages sous un même nom.
Les principales commandes sont:
- index=pvm_putinfo(name,msgbuf,flag) qui range dans la base
de données un message sous le nom name,
- index=pvm_recvinfo(name,index,flag) qui extrait de la base
de données un message de nom name,
- index=pvm_delinfo(name,index,flag) qui supprime de la base
de données un message de nom name.
B.2 MPI
MPI (``Message Passing Interface'')
est le prochain standard de fait (voir http: //www.mcs.anl.gov/mpi/).
MPI fait l'objet fin 1992 d'un groupe de travail
de 40 experts industriels et académiques.
Le premier standard MPI-1 est réalisé fin 1993.
Le principe est que chaque vendeur de supercalculateurs implémente
sa propre API propriétaire
en respectant le standard. Cela fait que
MPI est particulièrement bien optimisé sur un grand nombre d'architectures
de super-calculateurs.
Mais le système n'est pas basé comme PVM
sur la notion de machine virtuelle, et les implémentations sont moins bien
étudiées pour les réseaux de stations (éventuellement hétérogènes).
L'abstraction
utilisée par MPI est celle de topologie de passage de messages.
La deuxième version du standard a commencé début
1995, et le premier brouillon a été publié fin 1996.
B.2.1 MPI-1
Le premier standard décrit
128 fonctions, mais pas d'équivalent de pvm_spawn. Les processus sont
donc définis
de façon statique uniquement.
MPI-1 comprend une notion de communicator englobant les notions de
contextes (de PVM3.4) et les
groupes de processus (de PVM3.3).
La grande force du standard est le fait qu'il inclut énormément de
fonctions de communication point à point et
entre groupes de processus.
Ces fonctions reposent sur des fonctions de
définition de topologies de communication ainsi que
de définitions de datatypes qui décrivent des
messages pris à des endroits non contigus de la mémoire.
B.2.2 MPI-2
Le deuxième standard décrit
120 fonctions de plus et en particulier
des fonctions de communication collectives non-bloquantes
et un MPI_SPAWN (équivalent de pvm_spawn).
MPI-2 intègre également des communications unidirectionnelles
(les messages persistants de PVM3.4) et l'équivalent des handlers de PVM3.4.
MPI-2 devrait avoir des fonctions pour gérer la tolérance aux
pannes.
Il existe quelques versions gratuites actuellement implémentant en
partie les standards MPI dont,
- LAM (version 6.3 et 6.4a) qui implémente MPI-1 plus quelques fonctionnalités
MPI-2 (MPI_SPAWN etc.), voir
http://www.mpi.nd.edu/lam/
- MPICH (version ) qui implémente strictement MPI-1, voir
http://www.mcs.
anl.gov/mpi/mpich/
et ftp://info.mcs.anl.gov/pub/mpi
Sinon, il existe des versions contructeurs (non gratuites...)
sur CRAY, SGI, IBM etc.
B.2.3 Exemple
On donne ci-après un petit exemple de code MPI (fourni avec la distribution
standard).
/* Transmet un message dans un systeme a 2 processus. */
#include <mpi.h>
#define BUFSIZE 64
int buf[BUFSIZE];
int main(argc, argv)
int argc;
char *argv[];
{
int size, rank;
MPI_Status status;
/* Initialisation de MPI. */
MPI_Init(&argc, &argv);
/* On verifie le nombre de processus presents */
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (2 != size) {
MPI_Finalize(); /* comme pvm_exit */
return(1);
}
/* Determine son rang dans le groupe "World" */
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
/* L'envoyeur aura rang 0, le receveur, rang 1 */
/* Si rang 0 envoyer au rang 1 */
if (0 == rank) {
MPI_Send(buf, BUFSIZE, MPI_INT, 1, 11, MPI_COMM_WORLD);
}
/* Si rang 1 recevoir du rang 0 */
else {
MPI_Recv(buf,BUFSIZE,MPI_INT,0,11,MPI_COMM_WORLD,&status);
}
MPI_Finalize();
return(0);
}
Exemple:
#include <mpi.h>
#define WORKTAG 1
#define DIETAG 2
#define NUM_WORK_REQS 200
static void master();
static void slave();
/* Ce programme est MIMD, mais est ecrit SPMD pour */
/* etre plus simple a lancer */
int main(argc, argv)
int argc;
char *argv[];
{
int myrank;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, /* groupe de tout le monde */
&myrank); /* 0 a N-1 */
if (myrank == 0) {
master();
} else {
slave();
}
MPI_Finalize();
return(0);
}
/* Le maitre envoie des demandes de travaux aux */
/* esclaves puis collecte les resultats */
static void master()
{
int ntasks, rank, work;
double result;
MPI_Status status;
MPI_Comm_size(MPI_COMM_WORLD,&ntasks); /* #processus */
/* Initialiser les esclaves */
work = NUM_WORK_REQS; /* simulation */
for (rank = 1; rank < ntasks; ++rank) {
MPI_Send(&work, /* tampon de messages */
1, /* un seul element */
MPI_INT, /* de ce type */
rank, /* au proc de ce rang */
WORKTAG, /* message "travail" */
MPI_COMM_WORLD);/* contexte */
work--;
}
/* Recoit un resultat de n'importe quel esclave et */
/* renvoie un nouveau travail jusqu'a ce que les */
/* requetes soient epuisees */
while (work > 0) {
MPI_Recv(&result, /* tampon de messages */
1, /* un seul element */
MPI_DOUBLE, /* de ce type */
MPI_ANY_SOURCE, /* de n'importe qui */
MPI_ANY_TAG, /* n'importe quel message */
MPI_COMM_WORLD, /* communicator */
&status); /* info */
MPI_Send(&work, 1, MPI_INT, status.MPI_SOURCE,
WORKTAG, MPI_COMM_WORLD);
work--; /* simulation */
}
/* Recoit les resultats des esclaves */
for (rank = 1; rank < ntasks; ++rank) {
MPI_Recv(&result, 1, MPI_DOUBLE, MPI_ANY_SOURCE,
MPI_ANY_TAG, MPI_COMM_WORLD, &status);
}
/* Dit aux esclaves de terminer */
for (rank = 1; rank < ntasks; ++rank) {
MPI_Send(0, 0, MPI_INT, rank, DIETAG, MPI_COMM_WORLD);
}
}
/* Chaque esclave accepte les requetes de travaux et */
/* retourne les resultats jusqu'a requete terminaison */
static void slave()
{
double result;
int work;
MPI_Status status;
for (;;) {
MPI_Recv(&work, 1, MPI_INT, 0, MPI_ANY_TAG,
MPI_COMM_WORLD, &status);
/* Verifie le tag du message */
if (status.MPI_TAG == DIETAG) {
return;
}
sleep(2);
result = 6.0; /* simulation */
MPI_Send(&result, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
}
B.3 Comparaison PVM, MPI
En général MPI a de meilleures performances que PVM (mais
elles restent quand même très comparables même sur un Cray T3D ou un IBM SP2).
PVM sacrifie les performances pour être portable sur un réseau hétérogène.
Par contre MPI est non portable sur réseau de stations (pas de méthode
standard pour démarrer MPI, ni de notion de machine virtuelle).
MPI a plus de primitives de communications, mais il y a encore un
certain nombre de problèmes non-résolus
pour les pannes (en particulier vis-à-vis des communicators).
En conclusion, PVM3.4 et MPI-2 se ressemblent maintenant beaucoup,
aux performances près. Parmi les projets en cours, on trouve d'ailleurs
un projet de ``convergence'' PVM et MPI qui se nomme PVMMPI.