MPI/fr: Difference between revisions

Jump to navigation Jump to search
7,160 bytes added ,  5 years ago
Updating to match new version of source page
No edit summary
(Updating to match new version of source page)
Line 22: Line 22:
== Qu'est-ce que MPI? ==
== Qu'est-ce que MPI? ==


<div class="mw-translate-fuzzy">
MPI (''message passing interface'') est en réalité une norme avec des sous-routines, fonctions, objets et autres éléments pour développer des programmes parallèles dans un environnement à mémoire distribuée. MPI est implémentée dans plusieurs bibliothèques, notamment Open MPI, MPICH et MVAPICH. La norme décrit la méthode d'appel en Fortran, C et C++, mais il existe aussi des méthodes indirectes d'appel pour plusieurs autres langages (Boost.MPI, mpi4py, Rmpi, etc.).
MPI (''message passing interface'') est en réalité une norme avec des sous-routines, fonctions, objets et autres éléments pour développer des programmes parallèles dans un environnement à mémoire distribuée. MPI est implémentée dans plusieurs bibliothèques, notamment Open MPI, MPICH et MVAPICH. La norme décrit la méthode d'appel en Fortran, C et C++, mais il existe aussi des méthodes indirectes d'appel pour plusieurs autres langages (Boost.MPI, mpi4py, Rmpi, etc.).
</div>


Puisque MPI est une norme ouverte sans droits exclusifs, un programme MPI peut facilement être porté sur plusieurs ordinateurs différents. Les programmes MPI peuvent être exécutés concurremment sur plusieurs cœurs à la fois et offrent une parallélisation efficace, permettant une bonne [[Scalability/fr|scalabilité]]. Puisque chaque processus possède sa propre plage mémoire, certaines opérations de débogage s'en trouvent simplifié es; en ayant des plages mémoire distinctes, les processus n’auront aucun conflit d’accès à la mémoire comme c'est le cas en mémoire partagée. Aussi, en présence d'une erreur de segmentation, le fichier ''core'' résultant peut être traité par des outils standards de débogage série. Le besoin de gérer la communication et la synchronisation de façon explicite donne par contre l'impression qu'un programme MPI est plus complexe qu'un autre programme où la gestion de la communication serait implicite. Il est cependant recommandé de restreindre les communications entre processus pour favoriser la vitesse de calcul d'un programme MPI.
Puisque MPI est une norme ouverte sans droits exclusifs, un programme MPI peut facilement être porté sur plusieurs ordinateurs différents. Les programmes MPI peuvent être exécutés concurremment sur plusieurs cœurs à la fois et offrent une parallélisation efficace, permettant une bonne [[Scalability/fr|scalabilité]]. Puisque chaque processus possède sa propre plage mémoire, certaines opérations de débogage s'en trouvent simplifié es; en ayant des plages mémoire distinctes, les processus n’auront aucun conflit d’accès à la mémoire comme c'est le cas en mémoire partagée. Aussi, en présence d'une erreur de segmentation, le fichier ''core'' résultant peut être traité par des outils standards de débogage série. Le besoin de gérer la communication et la synchronisation de façon explicite donne par contre l'impression qu'un programme MPI est plus complexe qu'un autre programme où la gestion de la communication serait implicite. Il est cependant recommandé de restreindre les communications entre processus pour favoriser la vitesse de calcul d'un programme MPI.
Line 28: Line 30:
Nous verrons plus loin quelques-uns de ces points et proposerons des stratégies de solution; les références mentionnées au bas de cette page sont aussi à consulter.
Nous verrons plus loin quelques-uns de ces points et proposerons des stratégies de solution; les références mentionnées au bas de cette page sont aussi à consulter.


<div class="mw-translate-fuzzy">
== Principes de base ==
== Principes de base ==
Dans ce tutoriel, nous présenterons le développement d'un code MPI en C et en Fortran, mais les différents principes de communication s'appliquent à tout langage et à toute bibliothèque permettant une utilisation indirecte de l'API de MPI. Notre but ici est de paralléliser le programme simple "Hello World" utilisé dans les exemples.
Dans ce tutoriel, nous présenterons le développement d'un code MPI en C et en Fortran, mais les différents principes de communication s'appliquent à tout langage et à toute bibliothèque permettant une utilisation indirecte de l'API de MPI. Notre but ici est de paralléliser le programme simple "Hello World" utilisé dans les exemples.
</div>
<tabs>
<tabs>
<tab name="C">
<tab name="C">
Line 71: Line 75:
   
   
  end program hello
  end program hello
}}
</tab>
<tab name="Python">
{{File
  |name=hello.py
  |lang="python"
  |contents=
print('Hello, world!')
}}
}}
</tab>
</tab>
Line 86: Line 98:
[[Image:SPMD_model.png|frame|center|'''Figure 3''': ''Contrôle de comportements divergents'']]
[[Image:SPMD_model.png|frame|center|'''Figure 3''': ''Contrôle de comportements divergents'']]


<div class="mw-translate-fuzzy">
=== Cadre d'exécution ===  
=== Cadre d'exécution ===  
Un programme MPI importe le fichier entête approprié (<tt>mpi.h</tt> en C/C++; <tt>mpif.h</tt> en Fortran); il peut donc être compilé puis relié à l'implémentation MPI de notre choix. Dans la plupart des cas, l'implémentation possède un script pratique qui enveloppe l'appel au compilateur (''compiler wrapper'') et qui configure adéquatement <code>include</code> et <code>lib</code>, entre autres pour relier les indicateurs. Nos exemples utilisent les scripts de compilation suivants&nbsp;:  
Un programme MPI importe le fichier entête approprié (<tt>mpi.h</tt> en C/C++; <tt>mpif.h</tt> en Fortran); il peut donc être compilé puis relié à l'implémentation MPI de notre choix. Dans la plupart des cas, l'implémentation possède un script pratique qui enveloppe l'appel au compilateur (''compiler wrapper'') et qui configure adéquatement <code>include</code> et <code>lib</code>, entre autres pour relier les indicateurs. Nos exemples utilisent les scripts de compilation suivants&nbsp;:  
Line 91: Line 104:
* pour le Fortran, <tt>mpif90</tt>
* pour le Fortran, <tt>mpif90</tt>
* pour le C++, <tt>mpiCC</tt>
* pour le C++, <tt>mpiCC</tt>
</div>


Une fois les instances lancées, elles doivent se coordonner, ce qui se fait en tout premier lieu par l'appel d'une fonction d'initialisation&nbsp;:
Une fois les instances lancées, elles doivent se coordonner, ce qui se fait en tout premier lieu par l'appel d'une fonction d'initialisation&nbsp;:
Line 108: Line 122:
  MPI_INIT(IERR)
  MPI_INIT(IERR)
  INTEGER :: IERR
  INTEGER :: IERR
</source>
</tab>
<tab name="Fortran 2008">
<source lang="fortran">
MPI_Init(ierr)
INTEGER, OPTIONAL, INTENT(OUT) :: ierr
</source>
</tab>
<tab name="Python (mpi4py)">
<source lang="Python">
# importing automatically initializes MPI with mpi4py
MPI.Init()
  </source>
  </source>
</tab>
</tab>
</tabs>
</tabs>
<div class="mw-translate-fuzzy">
En C, les arguments de <code>MPI_Init</code>  pointent vers les variables  <code>argc</code> et <code>argv</code> qui sont les arguments en ligne de commande. Comme pour toutes les fonctions MPI en C, la valeur retournée représente l'erreur de la fonction. En Fortran, les routines MPI retournent l'erreur dans l'argument <code>IERR</code>.
En C, les arguments de <code>MPI_Init</code>  pointent vers les variables  <code>argc</code> et <code>argv</code> qui sont les arguments en ligne de commande. Comme pour toutes les fonctions MPI en C, la valeur retournée représente l'erreur de la fonction. En Fortran, les routines MPI retournent l'erreur dans l'argument <code>IERR</code>.
</div>


On doit aussi appeler la fonction <code>MPI_Finalize</code> pour faire un nettoyage avant la fin du programme, le cas échéant&nbsp;:
On doit aussi appeler la fonction <code>MPI_Finalize</code> pour faire un nettoyage avant la fin du programme, le cas échéant&nbsp;:
Line 128: Line 156:
  INTEGER :: IERR
  INTEGER :: IERR
</source>
</source>
</tab>
<tab name="Fortran 2008">
<source lang="fortran">
MPI_Finalize(ierr)
INTEGER, OPTIONAL, INTENT(OUT) :: ierr
</source>
</tab>
<tab name="Python (mpi4py)">
<source lang="Python">
# mpi4py installs a termination hook so there is no need to explicitly call MPI.Finalize.
MPI.Finalize()
</source>
</tab>
</tab>
</tabs>
</tabs>
Line 176: Line 216:
program phello0
program phello0
   
   
     include "mpif.h"
     use mpi
    implicit none
   
   
     integer :: ierror
     integer :: ierror
Line 185: Line 226:


  end program phello0
  end program phello0
}}
</tab>
<tab name="Fortran 2008">
{{File
  |name=phello0.f90
  |lang="fortran"
  |contents=
program phello0
    use mpi_f08
    implicit none
    call MPI_Init()
    print *, 'Hello, world!'
    call MPI_Finalize()
end program phello0
}}
</tab>
<tab name="Python (mpi4py)">
{{File
  |name=phello0.py
  |lang="python"
  |contents=
from mpi4py import MPI
print('Hello, world!')
}}
}}
</tab>
</tab>
Line 210: Line 277:
  MPI_COMM_RANK(COMM, RANK, IERR)
  MPI_COMM_RANK(COMM, RANK, IERR)
  INTEGER :: COMM, RANK, IERR
  INTEGER :: COMM, RANK, IERR
</source>
</tab>
<tab name="Fortran 2008">
<source lang="fortran">
MPI_Comm_size(comm, size, ierr)
TYPE(MPI_Comm), INTENT(IN) :: comm
INTEGER, INTENT(OUT) :: size
INTEGER, OPTIONAL, INTENT(OUT) :: ierr
MPI_Comm_rank(comm, rank, ierr)
TYPE(MPI_Comm), INTENT(IN) :: comm
INTEGER, INTENT(OUT) :: rank
INTEGER, OPTIONAL, INTENT(OUT) :: ierr
</source>
</tab>
<tab name="Python (mpi4py)">
<source lang="python">
MPI.Intracomm.Get_rank(self)
MPI.Intracomm.Get_size(self)
</source>
</source>
</tab>
</tab>
Line 269: Line 356:
  program phello1
  program phello1
   
   
     include "mpif.h"
     use mpi
    implicit none
 
     integer :: rank, size, ierror
     integer :: rank, size, ierror
   
   
Line 282: Line 370:
   
   
  end program phello1
  end program phello1
}}
</tab>
<tab name="Fortran 2008">
{{File
  |name=phello1.f90
  |lang="fortran"
  |contents=
program phello1
    use mpi_f08
    implicit none
    integer :: rank, size
    call MPI_Init()
    call MPI_Comm_size(MPI_COMM_WORLD, size)
    call MPI_Comm_rank(MPI_COMM_WORLD, rank)
    print *, 'Hello from process ', rank, ' of ', size
    call MPI_Finalize(ierror)
end program phello1
}}
</tab>
<tab name="Python (mpi4py)">
{{File
  |name=phello1.py
  |lang="python"
  |contents=
from mpi4py import MPI
comm = MPI.COMM_WORLD
size = comm.Get_size()
rank = comm.Get_rank()
print('Hello from process %d of %d'%(rank, size))
}}
}}
</tab>
</tab>
Line 298: Line 423:
Pour compiler avec Boost&nbsp;:  
Pour compiler avec Boost&nbsp;:  
  [~]$ mpic++ --std=c++11 phello1.cpp -lboost_mpi-mt -lboost_serialization-mt -o phello1
  [~]$ mpic++ --std=c++11 phello1.cpp -lboost_mpi-mt -lboost_serialization-mt -o phello1
If you are using the Python version, you don't need to compile but can run with:
[~]$ mpirun -np 4 python phello1.py


=== Communication ===
=== Communication ===
Line 340: Line 468:
  <type> MESSAGE(*)
  <type> MESSAGE(*)
  INTEGER :: COUNT, DATATYPE, DEST, TAG, COMM, IERR
  INTEGER :: COUNT, DATATYPE, DEST, TAG, COMM, IERR
</source>
</tab>
<tab name="Fortran 2008">
<source lang="fortran">
MPI_Send(message, count, datatype, dest, tag, comm, ierr)
TYPE(*), DIMENSION(..), INTENT(IN) :: message
INTEGER, INTENT(IN) :: count, dest, tag
TYPE(MPI_Datatype), INTENT(IN) :: datatype
TYPE(MPI_Comm), INTENT(IN) :: comm
INTEGER, OPTIONAL, INTENT(OUT) :: ierr
</source>
</tab>
<tab name="Python (mpi4py)">
<source lang="python">
# For general Python objects (pickled)
MPI.Intracomm.send(self, obj, int dest, int tag=0)
# For numpy arrays (fast)
MPI.Intracomm.Send(self, buf, int dest, int tag=0)
</source>
</source>
</tab>
</tab>
Line 346: Line 493:
En Fortran&nbsp;:<tt>MPI_CHARACTER</tt>, <tt>MPI_INTEGER</tt>, <tt>MPI_REAL</tt>, etc. Pour la liste complète des types de données, consultez la section Références en bas de page.
En Fortran&nbsp;:<tt>MPI_CHARACTER</tt>, <tt>MPI_INTEGER</tt>, <tt>MPI_REAL</tt>, etc. Pour la liste complète des types de données, consultez la section Références en bas de page.


<div class="mw-translate-fuzzy">
À la fonction de réception <tt>MPI_Recv</tt>, on ajoute l'argument  <tt>status</tt>&nbsp;:  en C, l'argument réfère à une structure allouée <tt>MPI_Status</tt> et en Fortran, l'argument contient une matrice <tt>MPI_STATUS_SIZE</tt> de nombres entiers. À son retour, <tt>MPI_Recv</tt>  contiendra de l'information sur le message reçu. Nos exemples ne montrent pas cet argument, mais il doit faire partie des instructions.
À la fonction de réception <tt>MPI_Recv</tt>, on ajoute l'argument  <tt>status</tt>&nbsp;:  en C, l'argument réfère à une structure allouée <tt>MPI_Status</tt> et en Fortran, l'argument contient une matrice <tt>MPI_STATUS_SIZE</tt> de nombres entiers. À son retour, <tt>MPI_Recv</tt>  contiendra de l'information sur le message reçu. Nos exemples ne montrent pas cet argument, mais il doit faire partie des instructions.
</div>
<tabs>
<tabs>
<tab name="C">
<tab name="C">
Line 376: Line 525:
  <type> :: MESSAGE(*)
  <type> :: MESSAGE(*)
  INTEGER :: COUNT, DATATYPE, SOURCE, TAG, COMM, STATUS(MPI_STATUS_SIZE), IERR
  INTEGER :: COUNT, DATATYPE, SOURCE, TAG, COMM, STATUS(MPI_STATUS_SIZE), IERR
</source>
</tab>
<tab name="Fortran 2008">
<source lang="fortran">
MPI_Recv(message, count, datatype, source, tag, comm, status, ierr)
TYPE(*), DIMENSION(..) :: message
INTEGER, INTENT(IN) :: count, source, tag
TYPE(MPI_Datatype), INTENT(IN) :: datatype
TYPE(MPI_Comm), INTENT(IN) :: comm
TYPE(MPI_Status) :: status
INTEGER, OPTIONAL, INTENT(OUT) :: ierr
</source>
</tab>
<tab name="Python (mpi4py)">
<source lang="python">
# For general Python objects (pickled)
MPI.Intracomm.recv(self, buf=None, int source=ANY_SOURCE, int tag=ANY_TAG, Status status=None)
# For numpy arrays (fast)
MPI.Intracomm.Recv(self, buf, int source=ANY_SOURCE, int tag=ANY_TAG, Status status=None)
</source>
</source>
</tab>
</tab>
Line 462: Line 631:


     implicit none
     implicit none
     include 'mpif.h'
     use mpi
     integer, parameter :: BUFMAX=81
     integer, parameter :: BUFMAX=81
     character(len=BUFMAX) :: outbuf, inbuf, tmp
     character(len=BUFMAX) :: outbuf, inbuf, tmp
Line 491: Line 660:
end program phello2
end program phello2
}}
}}
</tab>
<tab name="Fortran 2008">
{{File
  |name=phello2.f90
  |lang="fortran"
  |contents=
program phello2
    implicit none
    use mpi_f08
    integer, parameter :: BUFMAX=81
    character(len=BUFMAX) :: outbuf, inbuf, tmp
    integer :: rank, num_procs
    integer :: sendto, recvfrom
    type(MPI_Status) :: status
    call MPI_Init()
    call MPI_Comm_rank(MPI_COMM_WORLD, rank)
    call MPI_Comm_size(MPI_COMM_WORLD, num_procs)
    outbuf = 'Hello, world! from process '
    write(tmp,'(i2)') rank
    outbuf = outbuf(1:len_trim(outbuf)) // tmp(1:len_trim(tmp))
    write(tmp,'(i2)') num_procs
    outbuf = outbuf(1:len_trim(outbuf)) // ' of ' // tmp(1:len_trim(tmp))
    sendto = mod((rank + 1), num_procs)
    recvfrom = mod((rank + num_procs - 1), num_procs)
    call MPI_Send(outbuf, BUFMAX, MPI_CHARACTER, sendto, 0, MPI_COMM_WORLD)
    call MPI_Recv(inbuf, BUFMAX, MPI_CHARACTER, recvfrom, 0, MPI_COMM_WORLD, status)
    print *, 'Process', rank, ': Process', recvfrom, ' said:', inbuf
    call MPI_Finalize()
end program phello2
}}
</tab>
<tab name="Python (mpi4py)">
{{File
  |name=phello2.py
  |lang="python"
  |contents=
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
outbuf = "Hello, world! from process %d of %d" % (rank, size)
sendto = (rank + 1) % size;
recvfrom = (rank + size - 1) % size;
comm.send(outbuf, dest=sendto, tag=0)
inbuf = comm.recv(source=recvfrom, tag=0)
print('[P_%d] process %d said: "%s"]' % (rank, recvfrom, inbuf))
</tab>
</tab>
</tabs>
</tabs>
Line 659: Line 887:


     implicit none
     implicit none
     include 'mpif.h'
     use mpi


     integer, parameter :: BUFMAX=81
     integer, parameter :: BUFMAX=81
Line 693: Line 921:


end program phello3
end program phello3
}}
</tab>
<tab name="Fortran 2008">
{{File
  |name=phello3.f90
  |lang="fortran"
  |contents=
program phello3
    implicit none
    use mpi_f08
    integer, parameter :: BUFMAX=81
    character(len=BUFMAX) :: outbuf, inbuf, tmp
    integer :: rank, num_procs
    integer :: sendto, recvfrom
    type(MPI_Status) :: status
    call MPI_Init()
    call MPI_Comm_rank(MPI_COMM_WORLD, rank)
    call MPI_Comm_size(MPI_COMM_WORLD, num_procs)
    outbuf = 'Hello, world! from process '
    write(tmp,'(i2)') rank
    outbuf = outbuf(1:len_trim(outbuf)) // tmp(1:len_trim(tmp))
    write(tmp,'(i2)') num_procs
    outbuf = outbuf(1:len_trim(outbuf)) // ' of ' // tmp(1:len_trim(tmp))
    sendto = mod((rank + 1), num_procs)
    recvfrom = mod(((rank + num_procs) - 1), num_procs)
    if (MOD(rank,2) == 0) then
        call MPI_Send(outbuf, BUFMAX, MPI_CHARACTER, sendto, 0, MPI_COMM_WORLD)
        call MPI_Recv(inbuf, BUFMAX, MPI_CHARACTER, recvfrom, 0, MPI_COMM_WORLD, status)
    else
        call MPI_RECV(inbuf, BUFMAX, MPI_CHARACTER, recvfrom, 0, MPI_COMM_WORLD, status)
        call MPI_SEND(outbuf, BUFMAX, MPI_CHARACTER, sendto, 0, MPI_COMM_WORLD)
    endif
    print *, 'Process', rank, ': Process', recvfrom, ' said:', inbuf
    call MPI_Finalize()
end program phello3
}}
</tab>
<tab name="Python (mpi4py)">
{{File
  |name=phello3.py
  |lang="python"
  |contents=
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
outbuf = "Hello, world! from process %d of %d" % (rank, size)
sendto = (rank + 1) % size;
recvfrom = ((rank + size) - 1) % size;
if rank % 2 == 0:
    comm.send(outbuf, dest=sendto, tag=0)
    inbuf = comm.recv(source=recvfrom, tag=0)
else:
    inbuf = comm.recv(source=recvfrom, tag=0)
    comm.send(outbuf, dest=sendto, tag=0)
print('[P_%d] process %d said: "%s"]' % (rank, recvfrom, inbuf))
}}
}}
</tab>
</tab>
38,757

edits

Navigation menu