390 likes | 561 Views
MPI. Message-Passing Programming. Le passage de messages. Processus. Le nombre est déterminé au début de l’exécution Demeure constant en cours d’exécution Tous les processus exécutent le même programme Chaque processus possède son propre ID
E N D
MPI Message-Passing Programming
Processus • Le nombreestdéterminé au début de l’exécution • Demeure constant en coursd’exécution • Tous les processusexécutent le mêmeprogramme • Chaqueprocessuspossède son propre ID • Le passage de messages sertà la communication et à la synchronisation • Même un message vide a une signification
Particularités du modèlepar passage de messages • Portabilité • Fonctionneaussibiensur des multi-ordinateursquesur des multiprocesseurs • Facilité de déboguage • Aucune variable partagée • Sur les multi-ordinateurs le temps de communication estélevé • On doitminimiser la communication • Moins de fautes de cache quesur les multiprocesseurs
Non satisfait Exemple: Satisfaisabilité de circuits 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Exemple: Satisfaisabilité de circuits • Le problème de la satisfaction de circuits est NP-complet • Aucunalgorithme polynomial connu • Force brute: On regardetoutes les solutions possibles: Inefficace • 16 bits en entrée 216 = 65,536 combinaisonsà tester
Allocation cyclique des entrées aux processus • On suppose qu’ily a pprocessus • On distribue les entrées aux processus de façoncyclique • Exemple: 5 processus et 12 entrées: • P0: 0, 5, 10 • P1: 1, 6, 11 • P2: 2, 7 • P3: 3, 8 • P4: 4, 9
Quiz Supposonsqu’ilyaitpprocessus et n entrées possibles et quel’allocationsoitcyclique. • Quelnombre maximum d’entréesva-t-onallouerà un processus? • Quelnombre minimum d’entréesva-t-onallouerà un processus? • Combien de processus se voientallouer le nombre maximum d’entrées?
Stratégiegénérale • Le programmeconsidère les 65,536 entrées possiblessur 16 bits • Les entrées sontallouées de façoncyclique • Chaqueprocessus examine chacune des entrées qui luisontallouées • Chaque entrée satisfaisant le circuit estaffichée.
Fichiersàinclure #include <mpi.h> #include <stdio.h>
Variables int main (intargc, char *argv[]) { inti; int id; /* Rang du processus*/ int p; /* Nombre de processus*/ void check_circuit (int, int); • argc et argvsontnécessaire pour initialiser MPI • Chaqueprocessuspossèdesaproprecopie des variables puisqu’ilexécutesaproprecopie du programme.
Initialisation de MPI • Utiliseravanttouteautrefonction MPI • Pas nécessairement la première instruction • Permetd’effectuer les réglagesnécessairesàl’utilisation de MPI • Analogie: Constructeur en POO MPI_Init (&argc, &argv);
Communicators • Communicator: Groupe de processus • Objet opaque offrantl’environnementnécessaire au passage de messages entre les processus • MPI_COMM_WORLD • Communicator par défaut • Contienttous les processus • Il est possible de créerd’autres communicators
Nom du Communicator Communicator Processus Rangs Communicator MPI_COMM_WORLD 0 5 2 1 4 3
Déterminer le nombre de precessus • Premier paramètre: nom du communicator • Valeur de retour: Code d’erreur • Nombre de processus: misàl’adresse &p intMPI_Comm_size(MPI_COMM_WORLD, &p);
Déterminer le rang d’un processus • Premier paramètre: Nom du communicator • Le rang (0, 1, …, p-1) estretournédans le second paramètre intMPI_Comm_rank(MPI_COMM_WORLD, &id);
2 0 5 4 3 1 id id id id id id 6 6 6 6 6 6 p p p p p p État des processus
Allocation cyclique for (i = id; i < 65536; i += p) check_circuit (id, i); • Le parallélisme se situeàl’extérieur de la fonctioncheck_circuit • check_circuitestunefonctionséquentielleordinaire
Terminerl’utilisation de MPI • Dernier appel MPI • Permet au système de libérer les ressources MPI • Analogie: Destructeur en POO MPI_Finalize();
#include <mpi.h>#include <stdio.h>int main (intargc, char *argv[]) {inti;int id;intp; void check_circuit (int, int);MPI_Init (&argc, &argv);MPI_Comm_rank (MPI_COMM_WORLD, &id);MPI_Comm_size (MPI_COMM_WORLD, &p); for (i = id; i < 65536; i += p)check_circuit (id, i);printf ("Process %d is done\n", id);fflush (stdout);MPI_Finalize(); return 0;}
/* Retourne1si le i-ième bit de ‘n’ est1; 0sinon*/ #define EXTRACT_BIT(n,i) ((n&(1<<i))?1:0) void check_circuit (int id, intz) { int v[16]; /* Each element is a bit of z */ inti; for (i = 0; i < 16; i++) v[i] = EXTRACT_BIT(z,i); if ((v[0] || v[1]) && (!v[1] || !v[3]) && (v[2] || v[3]) && (!v[3] || !v[4]) && (v[4] || !v[5]) && (v[5] || !v[6]) && (v[5] || v[6]) && (v[6] || !v[15]) && (v[7] || !v[8]) && (!v[7] || !v[13]) && (v[8] || v[9]) && (v[8] || !v[9]) && (!v[9] || !v[10]) && (v[9] || v[11]) && (v[10] || v[11]) && (v[12] || v[13]) && (v[13] || !v[14]) && (v[14] || v[15])) { printf ("%d) %d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d\n", id, v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9], v[10],v[11],v[12],v[13],v[14],v[15]); fflush (stdout); } }
Avant de compiler • ssh linuxmpi1.uqac.ca • lamboot: À utiliser par chaque usager. Cela sert à démarrer le deamon qui gère LAM/MPI. • lamhalt: Pour arrêter LAM/MPI • lamnodes: Pour connaître les processeursdisponibles • laminfo: Information sur la configuration de LAM/MPI • scp: Pour copier un fichierd’une machine àuneautre
Compiler un programme MPI • mpicc: pour compiler des fichiers C ou C++ utilisant MPI • Équivalentà: gcc -I/usr/local/include -pthread -L/usr/local/lib -llammpio -llamf77mpi -lmpi -llam -lutil –ldl • Faire mpicc –showmepour voirl’équivalence mpicc –off.c
Exécuter un programme MPI • mpirun –np <N> <exec> [<arg1> …] • mpirun –c <N> <exec> [<arg1> …] • -np <N> nombre de processus • -c <N> nombre de processus • <exec> exécutable • [<arg1> …] arguments de la ligne de commande
Permettre la connection à distance • MPI doitpouvoirdémarrer des processussurd’autresprocesseurs sans fournir de mot de passe • Chaqueprocesseurdans le groupedoitinclure la liste des autresprocesseursdans le fichier.rhosts • Par exemplesur linuxmpi1.uqac.ca (192.168.237.64): 192.168.237.65 192.168.237.66 192.168.237.67
Exécutionsur 1 CPU % mpirun -np 1 sat0) 1010111110011001 0) 0110111110011001 0) 1110111110011001 0) 1010111111011001 0) 0110111111011001 0) 1110111111011001 0) 1010111110111001 0) 0110111110111001 0) 1110111110111001 Process 0 is done
Exécutionsur 2 CPU % mpirun -np 2 sat0) 0110111110011001 0) 0110111111011001 0) 0110111110111001 1) 1010111110011001 1) 1110111110011001 1) 1010111111011001 1) 1110111111011001 1) 1010111110111001 1) 1110111110111001 Process 0 is done Process 1 is done
Exécutionsur 3 CPU % mpirun -np 3 sat0) 0110111110011001 0) 1110111111011001 2) 1010111110011001 1) 1110111110011001 1) 1010111111011001 1) 0110111110111001 0) 1010111110111001 2) 0110111111011001 2) 1110111110111001 Process 1 is done Process 2 is done Process 0 is done
Améliorer le programme • On veuttrouver le nombre total de solutions • On ajouteuneréduction au programme • La réductionestuneopération collective qui impliqueune communication entre tous les processus
Modifications • check_circuit • Retourne 1 si le circuit estsatisfaisable avec l’entréedonnée • Retourne 0 sinon • Chaqueprocessuscomptelocalement le nombred’entréessatisfaisant le circuit • La réductionesteffectuéesurl’ensemble des résultats.
/* * Circuit Satisfiability, Version 2 * * This enhanced version of the program prints the * total number of solutions. */ #include "mpi.h" #include <stdio.h> int main (intargc, char *argv[]) { int count; /* Solutions found by this proc */ intglobal_count; /* Total number of solutions */ int i; int id; /* Processrank */ int p; /* Number of processes */ intcheck_circuit (int, int); MPI_Init (&argc, &argv); MPI_Comm_rank (MPI_COMM_WORLD, &id); MPI_Comm_size (MPI_COMM_WORLD, &p);
count = 0; for (i = id; i < 65536; i += p) count += check_circuit (id, i); MPI_Reduce (&count, &global_count, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); printf ("Process %d isdone\n", id); fflush (stdout); MPI_Finalize(); if (!id) printf ("There are %d different solutions\n", global_count); return 0; } /* FIN du main */ /* Return 1 if 'i'th bit of 'n' is 1; 0 otherwise */ #define EXTRACT_BIT(n,i) ((n&(1<<i))?1:0)
intcheck_circuit (int id, int z) { int v[16]; /* Eachelementis a bit of z */ int i; for (i = 0; i < 16; i++) v[i] = EXTRACT_BIT(z,i); if ((v[0] || v[1]) && (!v[1] || !v[3]) && (v[2] || v[3]) && (!v[3] || !v[4]) && (v[4] || !v[5]) && (v[5] || !v[6]) && (v[5] || v[6]) && (v[6] || !v[15]) && (v[7] || !v[8]) && (!v[7] || !v[13]) && (v[8] || v[9]) && (v[8] || !v[9]) && (!v[9] || !v[10]) && (v[9] || v[11]) && (v[10] || v[11]) && (v[12] || v[13]) && (v[13] || !v[14]) && (v[14] || v[15])) { printf ("%d) %d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d\n", id, v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9], v[10],v[11],v[12],v[13],v[14],v[15]); fflush (stdout); return 1; } else return 0; }
Prototype de MPI_Reduce() intMPI_Reduce ( void *operand, /* Adresse du tableau d’entrée*/ void *result, /* Adresse du tableau des résultat*/intnbre, /* nombre de réductionsàeffectuer*/MPI_Datatype type, /* type des éléments*/MPI_Op operator, /* Opérateur de réduction*/ int root, /* Rang du processus qui reçoit le résultat*/ MPI_Commcomm /* communicator */ )
MPI_Datatype • MPI_CHAR • MPI_DOUBLE • MPI_FLOAT • MPI_INT • MPI_LONG • MPI_LONG_DOUBLE • MPI_SHORT • MPI_UNSIGNED_CHAR • MPI_UNSIGNED • MPI_UNSIGNED_LONG • MPI_UNSIGNED_SHORT
MPI_Op • MPI_BAND • MPI_BOR • MPI_BXOR • MPI_LAND • MPI_LOR • MPI_LXOR • MPI_MAX • MPI_MAXLOC • MPI_MIN • MPI_MINLOC • MPI_PROD • MPI_SUM
Exécution du second programme % mpirun -np 3 seq20) 0110111110011001 0) 1110111111011001 1) 1110111110011001 1) 1010111111011001 2) 1010111110011001 2) 0110111111011001 2) 1110111110111001 1) 0110111110111001 0) 1010111110111001 Process 1 is done Process 2 is done Process 0 is done There are 9 different solutions
Analyse de la performance • MPI_Barrierbarrière de synchronization • MPI_Wtick temps en secondes entre 2 tics d’horloge • MPI_Wtime temps en secondesdepuis le dernier appel
Code double elapsed_time;… MPI_Init (&argc, &argv);MPI_Barrier (MPI_COMM_WORLD);elapsed_time = - MPI_Wtime();… MPI_Reduce (…);elapsed_time += MPI_Wtime();