mpi et programmation par passage de messages matthieu exbrayat maîtrise dinformatique
TRANSCRIPT
MPI et programmation
par passage de messages
Matthieu EXBRAYATMaîtrise d’informatique
Modèles mémoire• Shared memory
– Mémoire partagée, disques partagés– Implantations « haut de gamme »– Croissance limitée
• Shared disk– Mémoire répartie, – Implantation physique assez rare (ou ferme de
disques)• Shared nothing
– Mémoire et disques répartis– Cluster de PC, implantation courante
• Remarque– implantation physique vs implantation logique
Mémoire Répartie (shared nothing)
• Fonctionnement parallèle = envoi de messages– Envoi / Réception– Permet la synchronisation
• Avantages– Portabilité, extensibilité
• Inconvénients– Gestion explicite des appels distants =
programmation à 2 niveaux
MPI : Message Passing Interface
• MPI = spécification standard (consortium)• Plusieurs implantations
– Gratuites– Portables
• Intérêt– Cadre « simple » pour la programmation par envoi de
messages– Sémantique « maîtrisée »– Portabilité et stabilité (protocole bas niveau pas géré
par développeur)– Permet architecture hétérogène
MPI Contient
• Constantes, types et fonctions pour C, C++, Fortran (77 et 90)
• Primitives de comm. point à point
• Primitives de comm. collectives
• Regroupement de processus
• Interface de suivi des performances
MPI ne contient pas
• Opérations en mémoire partagée
• EDI (programmation / débogage)– Il existe des extensions
• Gestion de processus– Lancement explicite au départ
• RPC
• Gestion des threads
• E/S Parallèles (disponible dans MPI 2)
Principe d’un programme MPI
• On écrit un programme contenant les opérations locales + les échanges via MPI (initialisation et échanges)
• On compile• On lance par mpirun –np n monprog
– n=nombre de machines– La liste des machines peut être définie explicitement
• Mpi crée n copies de l’exécutable sur les machines cibles
• Exécution asynchrone des processus (+synchro sur échange messages)
• Terminaison des processus
Différents types d’appel
• Local (manipulation locale d’objets MPI)• Non-local : échange avec autre(s) processus
– Bloquant : le processus reprend la main après l’opération (ressources disponibles)
– Non-bloquant : le processus reprend la main avant la fin de l’opération (nécessite contrôle)
– Point-à-point : Echange entre deux processus– Collectif : Tous les processus du groupe appellent la
même fonction
• NB : nous utiliserons le groupe par défaut, qui regroupe tous les processus
MPI et C
• Les fonctions commencent par MPI_
• Elles retournent un code d’erreur (qui vaut MPI_SUCCESS en cas de réussite)
• Les arguments de type tableau sont indexés à partir de 0 (C classique…)
Connection et déconnection à MPI
• MPI_Init(&argc,&argv);
• MPI_Comm_size(MPI_COMM_WORLD,&num_procs);
• MPI_Comm_rank(MPI_COMM_WORLD,&myid);
• MPI_Get_processor_name(processor_name,&namelen);
• MPI_Finalize();
MPI_Send : envoi bloquant
• int MPI_Send(void *buf,int count,MPI_Datatype datatype,int dest, int tag,MPI_Comm comm)
• buf : adresse mémoire (début des informations à transmettre)
• count : nombre de valeurs à envoyer• datatype : type de valeur• dest : numéro du processus destinataire• tag : numéro associé au message• comm : communicateur = groupe de processus.
Nous utilisons le groupe global : MPI_COMM_WORLD
Des détails…
• Avant l’appel à MPI_Send, les infos à envoyer sont placées dans une zone de mémoire contigüe (tableau de taille count)
• Si count = 0 : pas de données (utilisable pour synchro)• Les types de données :
– MPI_CHAR signed char– MPI_SHORT signed short int– MPI_INT signed int– MPI_LONG signed long int– MPI_FLOAT float– MPI_DOUBLE double– MPI_PACKED
• Comment envoyer un seul entier ?
MPI_Receive : réception bloquante
• int MPI_Recv (void* buf,int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
• buf : emplacement où stocker les valeurs reçues• count : capacité de buf (erreur si débordement,
placement au début si remplissage incomplet)• source : machine émettrice (numéro ou
MPI_ANY_SOURCE)• tag : numéro ou MPI_ANY_TAG• status : infos sur la réception (structure) :
– status.MPI_SOURCE– status.MPI_TAG– status.MPI_ERROR– int MPI_Get_count(MPI_Status *status,MPI_DATATYPE
datatype, int *count))
Bloquant ?
• Une opération est bloquante dans le sens où elle ne termine qu’après avoir terminé l’utilisation du buffer :– send se termine après l’envoi effectif (!=
réception)– receive se termine après la réception effective– un send peut-il être lancé avant le receive
correspondant ?
Ordre d’acheminement
• MPI garanti l’ordre d’acheminement entre un émetteur et un destinataire donnés.
• L’ordre global n’est par contre pas garanti (plusieurs émetteurs vers un même destinataire, par exemple).
Envoi et réception combinés
• Si deux processus souhaitent échanger des informations, il est possible d’appeler MPI_Sendrecv
• int MPI_Sendrecv (void* sendbuf, int sendcount, MPI_Datatype sendtype, int dest, int sendtag,void* recvbuf, int recvcount, MPI_Datatype recvtype,int source, int recvtag, MPI_Comm comm,MPI_Status *status)
• Procède comme deux threads qui géreraient un envoi et une réception
• envoi et réception sont bloquants• Les buffers sont nécessairement différents (disjoints)• Les tags peuvent être différents.
Processus NULL
• On peut faire des envoi vers MPI_PROC_NULL (si destinataire inactif pour une raison donnée)
• Se termine dès que possible. Aucun effet
• Utilisable aussi en réception.
Envoi et réception non bloquants
• Le processus reprend la main dès que la demande d’envoi ou de réception est faite.
• Le processus contrôle ensuite si l’envoi ou la réception a été effectué
• Permet des recouvrement calcul-communication (particulièrement intéressant pour réception)
Syntaxe
• int MPI_ISend(void *buf,int count,MPI_Datatype datatype,int dest, int tag,MPI_Comm comm, MPI_Request *request)
• int MPI_IRecv (void* buf,int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request)
• int MPI_Wait(MPI_Request * request, MPI_Status * status)
• int MPI_Test(MPI_Request * request, int *flag, MPI_Status * status)
Opérations collectives
• MPI définit certains algorithmes parallèles de base : diffusion, réduction…
• Définition cohérente avec send et receive, mais :
• correspondance entre message et buffer de réception (taille)
• opérations bloquantes
• pas de tag.
Barrière de synchronisation
• Permet de s’assurer d’un point de synchro (car pas de certitudes à partir des send et receive, même bloquants).
• int MPI_Barrier(MPI_Comm comm)
• Opération globale au groupe de processus
Broadcast
• int MPI_Bcast(void * buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
• Envoi depuis le processus de rang root vers tous les autres
Gather
• Un processus collecte des informations venant de tous les processus et les assemble (dans un tableau)
• int MPI_Gather(void *sendbuf,int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm)
• Tous les processus (root y-compris) envoient des infos vers root.
• Le placement se fait dans l’ordre des rangs de processus.
• en général, recvcount = sendcount
Scatter
• Scatter : un processus distribue des informations (stockées dans un tableau) vers tous les processus
• int MPI_Scatter(void *sendbuf,int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm)
• Tous les processus (root y-compris) reçoivent des infos de root.
• La distribution se fait dans l’ordre des rangs de processus.
• en général, recvcount = sendcount
Reduce
• On dispose d’un certain nombre de calculs de préfixes pré-implantés
• int MPI_Reduce(void* sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)
• MPI_MAX, MPI_MIN, MPI_SUM, MPI_PROD• MPI_LAND, MPI_LOR, MPI_LXOR• MPI_BAND, MPI_BOR, MPI_BXOR• MPI_MAXLOC, MPI_MINLOC (paire
valeur/index)
Scan
• On dispose d’un certain nombre de calculs de préfixes pré-implantés
• int MPI_Scan(void* sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
• Mêmes opérateurs que pour Reduce