Parallel Programming: Message-Passing-Interface

Die Seite wird erstellt Paul-Luca Roth
 
WEITER LESEN
Vorlesung Rechnerarchitektur 2                                                                               Seite 9

                   Parallel Programming: Message-Passing-Interface
      MPI - das Message Passing Interface

      • Das MPI Forum trat offiziell im Jahre 1992 zusammen (SuperComputing ’92)
             • parmacs Gruppe (wesentliche Teile Kommunikatoren, Kartesische Topologien,...)
             • PVM Gruppe
      • Ziel: ein Standard für ein striktes Message-Passing Model,
      • kooperativer Datentransfer zwischen teilnehmenden Prozessen
      • keine I/O Spezifikation
      • nur C und Fortran 77 Unterstützung
      • der MPI Standard wurde 1994 freigegeben
      • eine portable Message Passing Umgebung mit klassischen und auch einigen (damals) neuen
        Features (abgeleitete Datentypen, Kommunikatoren...)
      • offene und vendor-spezifische Implementierungen waren schnell verfügbar
      • dieser Standard ist heute als MPI-1 bekannt

      MPI wurde sehr schnell der de-facto Standard für Message Passing auf Parallel Rech-
      nern

      MPI-2
      • Im Jahr 1995 trat das MPI Forum wieder zusammen, Ziel: Spezifizierung von MPI-2
      • Zunächst: Fehlerkorrekturen für MPI-1, Ergebnis: MPI-1.2
      • Neu:
             • Parallel I/O, genannt MPI-IO. Analogie: Nachrichten an Prozesse senden - Nachrichten an das
               Dateisystem senden
             • Remote Memory Operations (auch RDMA, one-sided communication). Hauptoperationen: put
               und get, direkter Zugriff auf Speicher in einem entfernten Knoten durch explizite Kommuni-
               kation
             • Dynamisches Prozess Management (MPI_Comm_Spawn), Standardisiertes Interface zum
               Starten von MPI jobs (mpiexec)
             • Erweiterung der Kollektiven Operationen
             • Unterstützung der Sprachen C++ und Fortran 90
             • Externes Interface für Debuggers, Profilers etc.
             • bessere Thread-Safety
      • MPI-2 Standardardisierung beendet im Jahr 1997, inzwischen auch viele Implementierun-
        gen
      • Hier: im wesentlichen MPI-1 Kommunikation, Implementierung (MPICH2) ist aber MPI-2

Lehrstuhl für Rechnerarchitektur - Universität Mannheim                                                      WS03/04
Vorlesung Rechnerarchitektur 2                                                            Seite 10

                                         Message-Passing-Interface
      MPI-Einführung

      Voraussetzungen im Source-Code für ein MPI Programm:
      • mpi.h includen
      • Die Kommandozeilenparameter des Programms müssen an MPI_Init übergeben werden
        (die Parameter werden automatisch von dem Run-Script mpirun zugewiesen). MPI_Init
        muß vor allen anderen MPI-Funktionen aufgerufen werden:
             MPI_Init(&argc,&argv);

      • Nach der letzten MPI-Funktion muß der Befehl MPI_Finalize aufgerufen werden, um der
        MPI-Umgebung das Programmende mitzuteilen. Nach dem Befehl MPI_Finalize darf
        keine andere MPI-Funktion mehr aufgerufen werden:
             MPI_Finalize();

      • Jeder Prozess kann mittels MPI-Funktionen feststellen, wieviele Prozesse es insgesamt
        gibt (MPI_Comm_size) und seine eindeutige Nummer ermitteln (MPI_Comm_rank):
             MPI_Comm_rank(MPI_COMM_WORLD, &rank);
             MPI_Comm_size(MPI_COMM_WORLD, &size);

      • Der Standard-Kommunikator ist MPI_COMM_WORLD. Es ist jederzeit möglich, neue
        Kommunikatoren zu erstellen und verschiedene Topologien auf diese Kommunikatoren
        zu mappen.

      //////////////////////////////////////////////////////////////////////
      // A very short MPI test routine to show the involved hosts
      //////////////////////////////////////////////////////////////////////
      #include 
      #include 
      #include 

      int main(int argc, char **argv)
      {
        int size,rank;
        char hostname[50];

        MPI_Init(&argc,&argv);
        MPI_Comm_rank(MPI_COMM_WORLD, &rank); //How many processes?
        MPI_Comm_size(MPI_COMM_WORLD, &size); //Who am I?
        gethostname (hostname, 50);
        printf ("Hello World! I'm number %2d of %2d running on host %s\n",
      rank, size, hostname);
        MPI_Finalize();
        return 0;
      }

Lehrstuhl für Rechnerarchitektur - Universität Mannheim                                   WS03/04
Vorlesung Rechnerarchitektur 2                                                                Seite 11

                                         Message-Passing-Interface
      Kompilieren/Starten eines MPI-Programmes

      Einlesen des Source-Scriptes für MPI:
      pcatoll03 # source /opt0/Administration/common/mpich_path
      Setting up MPI-environment for Linux
      Anlegen der MPD Konfigurations Datei:
           • .mpd.conf im Homeverzeichnis, Dateizugriffsrechte 0600.
           • Inhalt min. die Zeile password=
      Starten der MPI Process Daemons (MPD):
      pcatoll03 # mpdboot --totalnum=3 --file=machinefile
      1 out of 3 mpds started; waiting for more ...
      3 out of 3 mpds started
      Kompilieren des Programmes (anstatt von gcc):
      pcatoll03 # mpicc -Wall mpitest.c -o mpitest
      Starten des Programmes:
      pcatoll03 # mpiexec -np 4 ./mpitest
      Hello World! I’m number 0 of 4 running              on   host   pcatoll03
      Hello World! I’m number 2 of 4 running              on   host   pcatoll02
      Hello World! I’m number 1 of 4 running              on   host   pcatoll01
      Hello World! I’m number 3 of 4 running              on   host   pcatoll01
      Beachte:
             • Angabe der Anzahl Prozesse mit dem Parameter ’-np ’
             • Reihenfolge der Ausgabe!

      Beispiel für ein Machinefile:
      pcatoll04
      pcatoll06
      • MPICH2 basiert auf ssh, einloggen ohne Angabe des Passworts muss konfiguriert sein (sie-
        he      http://mufasa.ra.informatik.uni-mannheim.de/page.php?name=lsra/persons/hol-
        ger/HowTosPublic/ssh.html)
      • Der Node auf dem das Programm gestartet wird, ist immer mit beteiligt (hier: pcatoll03)
        und gleichzeitig der Root-Prozess (id=0).
      • Die restlichen benötigten Nodes (hier 3) werden aus dem machinefile gelesen, gegebenfalls
        werden auf einem Rechner mehrere Prozesse gestartet. In jedem Fall muss der totalnum Pa-
        rameter angegeben werden
      Weitere Kommandos für den Umgang mit dem MPD:
      •   mpdtrace - Zeigt alle Knoten an auf denen ein eigener MPD läuft
      •   mpdallexit - Beendet alle eigenen MPDs. Wichtig: Vor dem ausloggen ausführen!
      •   mpdringtest - Testet die Verbindung zwischen den einzelnen MPDs
      •   mpdhelp - Zeigt alle verfügbaren MPD Kommandos

Lehrstuhl für Rechnerarchitektur - Universität Mannheim                                       WS03/04
Vorlesung Rechnerarchitektur 2                                                                                Seite 12

                                         Message-Passing-Interface
      Kommunikationsverfahren (communication modes)

      Prinzipiell wird zwischen blockierendem und nicht-blockierendem Senden/Empfangen unter-
      schieden (blocking und non-blocking).
      Blocking Send/Receive:
           Ein blocking Send wird sofort ausgeführt, ohne auf den zugehörigen Receive zu warten. Beim Rück-
           sprung wurde die Nachricht garantiert versandt, der Buffer kann wieder verwendet werden. Ein blocking
           Send sagt nichts über den Empfang der Nachricht aus.
      Non-blocking Send/Receive:
           Ein non-blocking Send wird ebenfalls sofort ausgeführt, allerdings kann der Rücksprung sofort nach der
           Benachrichtigung des Systems über den Versand der Nachricht erfolgen. Daher ist der Buffer unter Um-
           ständen noch mit der Nachricht belegt. Man darf den Buffer erst wieder verwenden, wenn man über ent-
           sprechende Funktionen überprüft hat, daß die Nachricht versandt worden ist.
      Überprüfung des Status von non-blocking Send/Receive:
           Mittels der Funktionen MPI_Wait und MPI_Test kann man den Status einer non-blocking Kommunika-
           tion überprüfen. Jede non-blocking Kommunikation muss/sollte mit diesem Completion-Check abge-
           schlossen     werden,    um     eventuell   belegte     Systemresourcen     wieder    freizugeben
           (implementationsabhängig) und sicherzustellen, dass der Sendepuffer wieder verwendet werden kann
           (senden) bzw. die Nachricht komplett empfangen wurde.

      Send-Varianten (jeweils kombinierbar mit blockierenend/nicht-blockierenend):
      Synchronous Send/Receive(MPI_SSend, MPI_Issend):
           Bevor von A an B gesendet wird, muß B einen entsprechenden synchronous Receive gestartet haben. Erst
           dann wird die Nachricht versendet. Nach dem Aufruf der Send-/Receive-Funktion ist die Kommunikation
           garantiert abgeschossen und der Buffer kann wieder verwendet werden.
      Buffered-Send (MPI_BSend, MPI_Ibsend):
           Beim buffered-Send muss der User explizit Speicherplatz im System reservieren (MPI_Buffer_attach,
           MPI_Buffer_detach). Die Funktion ist nicht abhängig von einem passenden Receive.
      Standard-Send (MPI_Send, MPI_Isend):
           MPI kann die Nachricht puffern, muss aber nicht. Der Aufruf kann direkt nach Pufferung zurückkehren
           oder auch erst wenn die Nachricht empfangen wurde (d.h. ähnlich synchronem Senden).
      Ready-Send (MPI_RSend, MPI_Irsend):
           Hier muss der Anwender dafür sorgen, daß der entsprende Receive schon gestartet ist. So kann man
           Overhead durch Warten vermeiden und sofort mit dem Versenden beginnen. Sollte ein Ready-Send
           ohne Receive gestartet werden, ist das Verhalten undefiniert.

Lehrstuhl für Rechnerarchitektur - Universität Mannheim                                                       WS03/04
Vorlesung Rechnerarchitektur 2                                                                                  Seite 13

                                         Message-Passing-Interface
      Verschiedene Buffer-Typen in MPI

                     A
                        M               B
                  Process P         Process Q
                       User buffer only                                  A
                                                                               M

                                                                             T                    B

                                    S                                  Process P              Process Q
                     A                                                 Usage of a user-level buffer T
                        M              B
                  Process P        Process Q
                   Usage of a system buffer S

      User buffer only:
           Einfach für synchronem- und ready-Send, bei anderem Send ohne Zwischenspeicher u.U. nicht möglich.
      Usage of a system buffer S:
           Alternative für nicht synchrones- oder ready-Send. Hier wird die Nachricht zuerst in einen Zwischenspei-
           cher kopiert.
           Probleme: Zusätzlicher Overhead, Nachricht kann zu groß sein für Zwischenspeicherung (Maximal ver-
           fügbarer Systemspeicher nicht zugänglich für den User).
      Usage of a user-level buffer T:
           In diesem Falle sorgt der User für einen entsprechend großen Zwischenspeicher. Da jetzt der User weiß
           wieviel Speicher verfügbar ist, kann er die Nachricht gegebenenfalls in kleinere Stücke aufteilen (buffe-
           red send).
      Ganz anderer Ansatz:
           Verwendung von One-sided-Commmunication: Remote-Direct-Memory-Access (RDMA). Zu verglei-
           chen mit DMA innerhalb eines Systems, nur auf einen entfernten Rechner. Der entfernte Rechner muss
           vor der Kommunikation ein Memory-Window freigeben, in dem bestimmte Rechner lesen/schreiben
           dürfen. Andere Kommunikationspartner können dann mit Put/Get-Operationen auf diesen Speicherbe-
           reich zugreifen.

Lehrstuhl für Rechnerarchitektur - Universität Mannheim                                                         WS03/04
Vorlesung Rechnerarchitektur 2                                                   Seite 14

                                         Message-Passing-Interface
      Beispiel für eine Kommunikation mit MPI
      //////////////////////////////////////////////////////////////////////
      //// A very short MPI test routine to test message passing
      //////////////////////////////////////////////////////////////////////
      #include 
      #include 

      int main(int argc, char **argv)
      {
        int i, size, rank, signal;
        MPI_Status status;
        double starttime, endtime;

          //Initialisation of MPI
          MPI_Init (&argc, &argv);
          MPI_Comm_rank (MPI_COMM_WORLD, &rank);
          MPI_Comm_size (MPI_COMM_WORLD, &size);

          //Master: send to every proc and wait for answer
          if (rank == 0) {
            signal = 666;
            starttime=MPI_Wtime();

              for (i=1; i
Vorlesung Rechnerarchitektur 2                                                                Seite 15

                                         Message Passing Interface
      Communicators

      Ein Kommunikator spezifiziert eine Kommunikations-Domäne. Ein Kommunikator besizt
      zwei feste Attribute: eine Prozessgruppe und eine Topologie, die die logische Anordnung der
      Prozesse in der Gruppe beschreibt. MPI_COMM_WORLD ist der vordefinierte Kommunika-
      tor.
      Funktionen für Kommunikatoren:
           • MPI_Comm_size - liefert die Anzahl der Prozesse in einem Kommunikator
           • MPI_Comm_rank - liefert den Rang des aufrufenden Prozesses in einem Kom-
             munikator
           • MPI_Comm_compare - vergleicht zwei Kommunikatoren
           • MPI_Comm_dup - dupliziert einen Kommunikator
           • MPI_Comm_create - erzeugt einen neuen Kommunikator für eine gegebene Pro-
             zessgruppe
           • ver. Funktionen um Prozessgruppen zu manipulieren
      Beispiel siehe oben.
      (für mehr Information insbesondere zu Parametern, man !)

      Kommunikatoren können mit einer (virtuellen) Topologie versehen werden:
      • Kartesische Topologie, d.h. Gitter oder Torus
          • MPI_Cart_create - Erzeugt einen Kommunikator mit kartesischer Topologie,
             Dimension und Periodizität können angegeben werden
          • MPI_Dims_create/MPI_Cartdim_get/MPI_Cart_get/MPI_Cart_rank/MPI_Car
             t_coords - Hilfsfunktionen zum Umgang mit kartesischen Topologien
          • MPI_Cart_shift ( für MPI_Sendrecv) - Hilfsfunktion um source und destination
             ranks für einen shift zu erhalten
      • Allgemeine Topologie
          • MPI_Graph_Create - Erzeugt eine allgemeine Topologie auf Basis einer ange-
             gebenen Graphenbeschreibung
      • Die Implementierung kann eine optimierte Anordnung der Prozesse z.B. für ein 2-D Gitter
        vornehmen.
      MPI_Dims_create(size, 2,dims);
      MPI_Cart_create(MPI_COMM_WORLD,2,dims,period,reorder,&cart);

Lehrstuhl für Rechnerarchitektur - Universität Mannheim                                       WS03/04
Vorlesung Rechnerarchitektur 2                                                                  Seite 16

                                         Message Passing Interface
      Send/Receive Funktionen - Übersicht

      • Standard-Send (normalerweise ausreichend)
          • MPI_Send - Standard Send
          • MPI_Isend - non-blocking Standard Send
          • MPI_Wait / MPI_Test / MPI_Test_some / MPI_Wait_all /... - Funktionen, um
             zu testen das eine oder mehrere Operationen abgeschlossen wurden
      • Blocking Send-Varianten:
          • MPI_Bsend - Bufferend Send
          • MPI_Ssend - Synchronous Sned
          • MPI_Rsend - Ready Send
      • Non-blocking Send-Varianten:
          • MPI_Ibsend - non-blocking, buffered Send
          • MPI_Issend - non-blocking, synchronous Send
          • MPI_Irsend - non-blocking, ready Send
      Beispiel s.o.

      Receive Funktionen:
          • MPI_Recv - Blocking Receive
          • MPI_Irecv - Non-blocking Receive
          • MPI_Iprobe - Prüft, ob eine Nachricht jetzt empfangen werden könnte
      Senden und Receive in einem:
             • MPI_Sendrecv - Funktion, die eine Nachricht sendet und gleichzeitig eine Nach-
               richt empfängt. Kann zum Beispiel in einer Gitter-orientierten Applikation ge-
               nutzt werden, um mit einem Nachbarn Daten auszutauschen oder um einen
               Daten-Shift durchzuführen.

      MPI_Status status;

      MPI_Sendrecv(send_buf, count, MPI_BYTE, neighbor_rank, tag, recv_buf,
                    count, MPI_BYTE, neighbor_rank, tag, cart, &status);

Lehrstuhl für Rechnerarchitektur - Universität Mannheim                                         WS03/04
Vorlesung Rechnerarchitektur 2                                                                                                                                           Seite 17

                                                          Message Passing Interface
      Kollektive Operationen

      •       MPI_Barrier - Barriere für alle Prozesse eines Kommunikators
      •       MPI_Bcast - Broadcaste eine Nachricht an alle Prozesse eines Kommunikators
      •       MPI_Gather - "Sammle" ein Datum von allen Prozessen in einem Prozess ein
      •       MPI_Gatherv - wie MPI_Gather, aber flexibler, da unterschiedlich viele Daten von den
              einzelnen Prozessein eingesammelt werden kann
      •       MPI_Scatter - "Verteile" Daten an alle andere Prozesse
      •       MPI_Scatterv - entsprechend MPI_Gatherv
      •       MPI_Allgather - Verschicke ein Datum von jedem Prozess an jeden anderen Prozess, so-
              dass alle Prozesse am Ende jeweils ein Datum von jedem Prozess besitzten (wie
              MPI_Gather, aber alle Prozesse empfangen das Ergebnis, nicht nur ein Empfänger).
      •       MPI_Alltoall - Erweiterung von MPI_Allgather. Jeder Prozess sendet unterschiedliche Da-
              ten zu jedem empfangenden Prozess. Der j. Block gesendet von Prozess i wird von Prozess
              j empfangen und in den i. Empfangspuffer empfangen.
      •       MPI_Reduce und MPI_Allreduce - Führt eine globale Reduktionsoperation aus, zum Bei-
              piel eine globale Summe. Bei Allreduce wird das Ergebnis an alle Prozesse geliefert.
      •       ...

     Kollektive Operationen für eine Gruppe von 6 Prozessen
                  a0                                              a0                              a0 a1 a2 a3 a4 a5                        a0
                                                 broad-           a0                                                            scatter    a1
       Prozesse

                                                 cast             a0                                                                       a2
                                                                  a0                                                                       a3
                                                                  a0                                                                       a4
                                                                                                                                gather
                                                                  a0                                                                       a5

                       Daten

                  a0                                               a0   b0   c0   d0    e0   f0   a0   a1   a2   a3   a4   a5              a0   b0   c0   d0   e0   f0
                                                  all-
                  b0                                               a0   b0   c0   d0    e0   f0   b0   b1   b2   b3   b4   b5              a1   b1   c1   d1   e1   f1
                                                  gather                                                                        alltoall
                  c0                                               a0   b0   c0   d0    e0   f0   c0   c1   c2   c3   c4   c5              a2   b2   c2   d2   e2   f2
                  d0                                               a0   b0   c0   d0    e0   f0   d0   d1   d2   d3   d4   d5              a3   b3   c3   d3   e3   f3
                  e0                                               a0   b0   c0   d0    e0   f0   e0   e1   e2   e3   e4   e5              a4   b4   c4   d4   e4   f4
                  f0                                               a0   b0   c0   d0    e0   f0   f0   f1   f2   f3   f4   f5              a5   b5   c5   d5   e5   f5
                  Quelle:MPI - The Complete Reference, M.Snir et al., MIT Press, 1996

Lehrstuhl für Rechnerarchitektur - Universität Mannheim                                                                                                                  WS03/04
Vorlesung Rechnerarchitektur 2                                                              Seite 18

                                         Message Passing Interface
      Utility Funktionen / Diverses

      • MPI_Wtime - Liefert die vergangene Zeit seit einem Zeitpunkt in der Vergangenheit als
        eine double Nummer (in Sekunden)
      • MPI_Get_Processor_Name - liefert den Namen der Maschine auf der der Prozess läuft
      • MPI_Init / MPI_Finalize / MPI_Abort - Initialisiere, beende, breche ab

      MPI Zusammenfassung
      - MPI 1.2 enthält sehr viele Funktionen, auch manchmal etwas "exotischere". Die meisten
        Applikationen verwenden nur ein kleines Subset dieser Funktionen.
      - Inzwischen ist der MPI Standard 2 verabschiedet und auch implementiert. MPI-2 erweitert
        MPI noch weiter, zum Beispiel um one-sided Communications (Remote Memory Operati-
        ons), Parallel IO, Dynamic Process Management, ausserdem mehrere kleinere Änderun-
        gen.
      - wir verwenden MPICH2 Version 1.0

Lehrstuhl für Rechnerarchitektur - Universität Mannheim                                     WS03/04
Sie können auch lesen