Exécution et débogage de programmes - LabUnix

Transcription

Exécution et débogage de programmes - LabUnix
Exécution et débogage de programmes parallèles
MPI avec MPICH2
Table des matières
1 Introduction : Caractéristiques d'un cluster et de trex_cluster
2
2 Étapes pour l'exécution d'un programme MPI
3
3 Stratégie pour déboguer un programme parallèle
5
4 Erreurs typiques dans des programmes MPI
4.1 Erreurs conduisant à des interblocages (deadlock ) . . . . . . . . . . . . .
7
5 Options de compilation avec MPICH2
9
4.2 Erreurs conduisant à des résultats erronnés . . . . . . . . . . . . . . . . .
4.3 Comportements non spéciés de MPI . . . . . . . . . . . . . . . . . . . .
6 Options d'exécution avec MPICH2
7
7
7
12
1 Introduction : Caractéristiques d'un cluster et
de trex_cluster
Voici deux dénitions de ce qu'est un cluster :
Co-located collection of mass-produced computers and switches dedicated to
running parallel jobs. The computers typically do not have displays or keyboards [and] some of the computers may not allow users to login [except
head node]. All computers run the same OS and have identical disk images. [They also] use a faster, [generally] switched networked, e.g., gigabit
Ethernet. [Qui03]
Any collection of distinct computers that are connected and used as a parallel
computer, or to form a redundant system for higher availability. The computers in a cluster are not specialized to cluster computing. In other words,
the computer making up the cluster [. . . ] are not custom-built for use in the
cluster. [MSM05].
Un cluster une grappe de processeurs, une grappe de calcul est donc une
machine parallèle MIMD de type multi-ordinateurs ayant les caractéristiques suivantes :
• Intégration des diverses machines en un système unique, généralement avec un
réseau dédié.
• Les divers processeurs travaillent sur un même problème.
• Les divers noeuds sont des machines de faible coût, où chaque noeud pourrait
en théorie être utilisé comme une machine/station de travail normale. Toutefois,
en pratique, généralement un seul des noeuds le head node est accessible
directement aux usagers.
La machine trex_cluster
La machine que nous allons utiliser pour la partie du cours sur MPI est la machine
trex_cluster.labunix.uqam.ca, qui possède les caractéristiques suivantes :
• Machine Ciara Tech.
• 14 processeurs Intel Xeon.1
• Réseau Gigabit Ethernet (full duplex ).
• Système d'exploitation Linux CentOS 5.6.
1 Il
y avait initialement 16 processeurs, mais 2 sont maintenant hors-service.
2
2 Étapes pour l'exécution d'un programme MPI
Pour compiler et exécuter un programme MPI/C avec MPICH 2.0 (version MPI installée
sur trex_cluster, plusieurs étapes sont nécessaires, décrites plus bas. Notez toutefois
que des cibles make appropriées ont été dénies pour les laboratoires, donc il sut
généralement d'exécuter make compile puis make run.
1. Il faut compiler le programme sur le noeud maître (trex_cluster). Par exemple :
$ mpicc -o hello -std=c99 hello.c
2. Il faut copier l'exécutable sur les diérents noeuds. Un script cp-sur-blades.csh
a été déni pour ce faire, lequel script fait des appels à scp et copie l'exécutable à
la racine du répertoire. Par exemple :
$ cp hello ~
$ ./cp-sur-blades.sh ~/hello
*** On copie 'hello' sur les differentes blades
'hello' copie sur 14 machines
3. Il faut activer, si ce n'est pas déjà fait, le daemon MPI. Par exemple :
$ cat mpd.hosts
trex_cluster
blade03
blade04
blade05
blade06
blade07
blade09
blade10
blade11
blade12
blade13
blade14
blade15
blade16
$ mpdboot -n 4 -f mpd.hosts
$ mpdtrace
trex_cluster
blade05
blade04
blade03
Note : Un script activer-mpdboot.sh a aussi été déni pour faire ce travail. Par
exemple, pour utiliser huit (8) processeurs parmi tous les processeurs disponibles
(spéciés dans mpd.hosts) :
$ ./activer-mpdboot.sh 8 mpd.hosts
3
4. On lance l'exécution du programme en spéciant le nombre de processeurs qu'on
désire utiliser, et ce à partir de la racine du répertoire avec la commande
mpirun. Par exemple :
$ cd ~
$ mpirun -np 6 hello
trex_cluster
numProc = 0, nbProcs =
blade05
6
numProc = 1, nbProcs =
6
numProc = 2, nbProcs =
6
numProc = 3, nbProcs =
6
trex_cluster
numProc = 4, nbProcs =
6
blade04
blade03
blade05
numProc = 5, nbProcs =
6
---------------------------------------
Signalons que n'importe quel programme ou commande accessible sur toutes les
machines peut être exécuté avec mpirun :
$ mpirun
Mon Mar
Mon Mar
Mon Mar
-np 3 date
4 20:45:08 EST 2013
4 18:44:59 EST 2013
4 18:36:33 EST 2013
$ mpirun -np 2 ls -l hello
-rwxrwxr-x 1 tremblay_g tremblay_g 798799 Mar
-rwxrwxr-x 1 tremblay_g tremblay_g 798799 Mar
4 18:35 hello
4 20:43 hello
$ mpirun -np 2 uname -a
Linux blade05 2.6.18-92.el5 #1 SMP Tue Jun 10 18:49:47 EDT 2008 i686 i686 i386 GNU/Linux
Linux trex_cluster 2.6.18-238.el5 #1 SMP Thu Jan 13 16:24:47 EST 2011 i686 i686 i386 GNU/Li
5. Lorsqu'on a dénitivement terminé, on tue les démons permettant l'exécution
des programmes MPI. Par exemple :
$ mpdallexit
4
3 Stratégie pour déboguer un programme parallèle
This brings up one of the most important points to keep up in mind when
you're debugging a parallel program: Many (if not most) parallel program
bugs have nothing to do with the fact that the program is a parallel program.
Many (if not most) parallel program bugs are caused by the same mistakes
that cause serial program bugs. [Pac97]
Pour déboguer un programme parallèle, il est généralement préférable de procéder,
en gros, comme suit :
a. On vérie tout d'abord le bon fonctionnement du programme pour un unique
processus s'exécutant sur un unique processeur.
b. On vérie le bon fonctionnement pour deux ou plusieurs processus mais s'exécutant
sur un seul et unique processeur voir plus bas.
c. On vérie le bon fonctionnement du programme pour deux ou plusieurs processus
s'exécutant sur deux processeurs.
d. On vérie le bon fonctionnement avec plusieurs processus s'exécutant sur plusieurs
processeurs.
Autres trucs :
• Lorsqu'on utilise des printf pour générer une trace d'exécution, il est préférable
de mettre une instruction fflush immédiatement après le printf, pour assurer
que les impressions se fassent dans un ordre qui reète le plus possible l'exécution
réelle :
fflush( stdout );
Il faut aussi savoir que les programmes parallèles peuvent parfois contenir des
Heisenbug. Plus précisément, il peut arriver qu'un programme parallèle ne fonctionne pas par exemple, à cause d'erreurs de synchronisation entre processus mais que le programme fonctionne sans problème quand on rajoute des instructions
printf pour tenter de le déboguer!2
Lorsqu'on utilise des traces d'exécution, il faut aussi tenir compte du fait que la
quantité totale d'information générée sera multipliée par le nombre de processeurs
ce qui peut parfois rendre dicile de comprendre les informations ainsi produites.
2 On
parle de Heisenbug car l'eet d'observer le programme modie son comportement, dans l'esprit
du principe d'incertitude d'Heisenberg en physique.
5
• Toujours pour la génération de traces d'exécution avec printf, il est préférable
d'indiquer explicitement le numéro du processus générant un élément de la trace.
Si ce numéro est mis (en préxe) au début de la ligne, alors on peut ensuite utiliser
l'outil Unix sort pour produire une liste exacte de ce qui est imprimé par chaque
processus voir aussi plus bas pour une façon rapide d'indiquer les numéros de
processus avec l'option d'exécution -l.
• Lorsqu'une erreur d'exécution est générée par MPI (avec MPICH2), le message
contient, au début de la ligne, le numéro du processus (virtuel) et autres informations. Par exemple, le message suivant a été produit par le processus virtuel 0 :
rank 0 in job 1 trex_cluster_55002 caused collective abort of all ranks
exit status of rank 0: killed by signal 11
6
4 Erreurs typiques dans des programmes MPI
Note : Les deux premières sous-sections sont une traduction (partielle) de l'appendice C
du livre de M.J. Quinn [Qui03].
4.1 Erreurs conduisant à des interblocages (deadlock )
• Un seul processus exécute un opération de communication collective (e.g., MPI_Reduce,
MPI_Bcast, MPI_Scatter).
Une solution à ce problème est de ne jamais mettre des opérations de communication collective dans du code s'exécutant conditionnellement.
• Deux ou plusieurs processus essaient d'échanger de l'information, mais en utilisant
MPI_Recv, et ce avant que des appels appropriés à MPI_Send aient été eectués.
Diérentes solutions sont possibles, par exemple : toujours s'assurer d'eectuer les
MPI_Send avant les MPI_Recv ; utiliser plutôt des appels à l'opération MPI_Sendrecv ;
utiliser des appels à MPI_Irecv.
• Un processus essaie de recevoir des données d'un processeur, qui n'eectue jamais
d'envoi approprié.
Ceci est souvent causé par l'utilisation d'un mauvais numéro de processus. Lorsque
possible, il est préférable d'utiliser les opérations collectives de communication.
Si ce n'est pas possible, il faut s'assurer que le patron des échanges entre les
processus soit le plus simple possible.
• Un processus essaie de recevoir des données de lui-même.
4.2 Erreurs conduisant à des résultats erronnés
• Incohérence au niveau des types utilisés dans l'opération Send vs. Recv.
Pour éviter ce problème, il faut s'assurer au niveau du code source que chaque
opération d'envoi possède une unique opération de réception correspondante, et
vérier (et re-vérier) le code source.
• Arguments d'un appel à une opération de communication transmis dans le mauvais
ordre.
4.3 Comportements non spéciés de MPI
• The MPI specication purposefully does not mandate whether or not collective
communication operations have the side eect of synchronizing the processes over
which they operate.
Donc, des opérations telles que MPI_Bcast, MPI_Reduce, etc., peuvent, ou non,
créer une barrière de synchronisation entre les processus impliqués.
7
• According to the MPI standard, message buering may or may not occur when
processes communicate with each other using MPI_Send.
Cette question sera abordée plus en détail ultérieurement.
8
5 Options de compilation avec MPICH2
Deux options de compilation permettent d'aider au débogage de programmes MPI (avec
la mise en oeuvre MPICH2) :
• -mpe=mpitrace : Lorsque cette option est spéciée au moment de la compilation,
divers messages liés à l'exécution d'opérations MPI sont achés lors de l'exécution
du programme. Un exemple, pour deux processeurs, est illustré à la gure 1.
• -mpe=mpicheck : Lorsque cette option est spéciée à la compilation, diverses
vérications sont eectuées sur les communications collectives au moment de
l'exécution. Si des communications sont eectuées incorrectement par exemple,
types invalides, numéros de processus racine incohérents, etc. alors des messages
d'erreur sont achés, comme l'illustre la gure 2. Signalons que pour cet exemple, si les vérications des communications collectives n'avaient pas été activées,
le programme aurait terminé sans générer d'erreur au niveau des communications,
mais avec un résultat incorrect :(
9
$ make EXEC=min NP=2 run
*** On copie '/home/tremblay_g/min' sur les differentes blades
'/home/tremblay_g/min' copie sur 16 machines
Starting MPI_Init...
Starting MPI_Init...
[0] Ending MPI_Init
[0] Starting MPI_Comm_rank...
[0] Ending MPI_Comm_rank
[0] Starting MPI_Comm_size...
[0] Ending MPI_Comm_size
[0] Starting MPI_Barrier...
[1] Ending MPI_Init
[1] Starting MPI_Comm_rank...
[1] Ending MPI_Comm_rank
[1] Starting MPI_Comm_size...
[1] Ending MPI_Comm_size
[1] Starting MPI_Barrier...
[0] Ending MPI_Barrier
[0] Starting MPI_Scatter...
[1] Ending MPI_Barrier
[1] Starting MPI_Scatter...
[0] Ending MPI_Scatter
[1] Ending MPI_Scatter
[0] Starting MPI_Reduce...
[1] Starting MPI_Reduce...
[1] Ending MPI_Reduce
[1] Starting MPI_Finalize...
[0] Ending MPI_Reduce
[0] Starting MPI_Finalize...
[0] Ending MPI_Finalize
10 calcul(s) du min pour 16000 element(s) avec 2 processeur(s) en
[1] Ending MPI_Finalize
0.002 secondes => 1
Figure 1: Trace d'exécution produite avec l'option de compilation -mpe=mpitrace.
10
$ make EXEC=min NP=2 run
Starting MPI Collective and Datatype Checking!
Backtrace of the callstack at rank 1:
At [0]: min(CollChk_err_han+0xb9)[0x8050cf9]
At [1]: min(CollChk_same_root+0x5c)[0x8052d8c]
At [2]: min(MPI_Reduce+0x52)[0x8051092]
At [3]: min(main+0x20e)[0x8050b4b]
At [4]: /lib/libc.so.6(__libc_start_main+0xdc)[0x95adec]
At [5]: min[0x8050741]
Fatal error in MPI_Comm_call_errhandler:
Collective Checking: REDUCE (Rank 1) --> Root Parameter (0) is inconsistent with rank 0 (1)
[cli_1]: aborting job:
Fatal error in MPI_Comm_call_errhandler:
Collective Checking: REDUCE (Rank 1) --> Root Parameter (0) is inconsistent with rank 0 (1)
rank 1 in job 1 trex_cluster_55002 caused collective abort of all ranks
exit status of rank 1: return code 1
Figure 2: Message d'erreur produit par la vérication des communications (option de
compilation -mpe=mpicheck).
11
6 Options d'exécution avec MPICH2
Divers options peuvent être spéciés à mpirun pour aider à déboguer un programme :
• On peut demander, avec l'option -l, que chaque impression sur stdout soit
automatiquement préxée du numéro du processus : voir gure 3.
• On peut utiliser explicitement le débogueur gdb :
$ mpirun -gdb pgm
• On peut exécuter le programme en créant plusieurs processus, mais sur un noeud
unique, ou sur un petit nombre de noeuds. Pour ce faire, il faut lancer de mpdboot
avec une liste de machines ne contenant que les noeuds à utiliser, tel qu'illustré à
la gure 4.
Remarque : Dans les chiers makefile qui vous sont fournis, deux cibles utilisant
cette façon de faire sont dénies :
debug_un : Lance les NP processus sur un unique noeud.
debug_deux : Lance les NP processus sur deux processeurs.
Voir gure 5.
Références
[MSM05] T.G. Mattson, B.A. Sanders, and B.L. Massingill. Patterns for Parallel Programming. Addison-Wesley, 2005.
[Pac97]
P.S. Pacheco. Parallel Programming with MPI. Morgan Kaufman Publ., 1997.
[Qui03]
M.J. Quinn. Parallel Programming in C with MPI and OpenMP. McGrawHill, 2003.
12
Sans l'option -l :
==================
$ make EXEC=hello NP=3 run
trex_cluster
numProc = 0, nbProcs =
blade05
blade12
3
numProc = 1, nbProcs =
3
numProc = 2, nbProcs =
3
--------------------------------------Avec l'option -l :
==================
$ make EXEC=hello NP=3 run
0: trex_cluster
0:
numProc = 0, nbProcs = 3
0:
1: blade05
1:
numProc = 1, nbProcs = 3
1:
2: blade12
2:
numProc = 2, nbProcs = 3
2:
0: ---------------------------------------
Figure 3: Utilisation de l'option -l pour indiquer le numéro du processus sur les
lignes de sortie.
13
$ cat unemachine.txt
trex_cluster
$ mpdboot -f unemachine.txt
$ mpdtrace
trex_cluster
$ mpirun -l -np 5 hostname
0: trex_cluster
4: trex_cluster
2: trex_cluster
3: trex_cluster
1: trex_cluster
$ mpdallexit
$ cat deuxmachines.txt
trex_cluster
blade02
$ mpdboot -n 2 -f deuxmachines.txt
$ mpdtrace
trex_cluster
blade02
$ mpirun -l -n 5 hostname
0: trex_cluster
4: trex_cluster
2: trex_cluster
1: blade02
3: blade02
Figure 4: Création de plusieurs processus sur un noeud unique ou sur un groupe limité
de noeuds. Cet exemple illustre aussi l'utilisation de l'option -l et le fait que le
programme exécuté par mpirun peut aussi être une simple commande Unix. Quant à
la commande mpdtrace, elle permet d'obtenir les noms des noeuds qui font partie du
groupe de noeuds activés lors du dernier appel à mpdboot.
14
$ make EXEC=hello NP=3 debug_un
*** On active mpdboot avec un seul processeur
trex_cluster
numProc = 0, nbProcs = 3
trex_cluster
numProc = 1, nbProcs =
3
trex_cluster
numProc = 2, nbProcs =
3
--------------------------------------$ make EXEC=hello NP=3 debug_deux
*** On active mpdboot avec 2 processeurs
trex_cluster
numProc = 0, nbProcs = 3
blade02
numProc = 1, nbProcs =
3
trex_cluster
numProc = 2, nbProcs =
3
--------------------------------------$ make EXEC=hello NP=3 run
*** On active mpdboot avec 16 processeurs
trex_cluster
numProc = 0, nbProcs = 3
blade05
blade13
numProc = 1, nbProcs =
3
numProc = 2, nbProcs =
3
---------------------------------------
Figure 5: Exemples d'exécution avec les cibles debug_un, debug_deux vs. run.
15

Documents pareils