MPI (Message Passing Interface)
Einleitung:
Message Passing Interface Progrmmier Model: Verteilter Speicher. Prozesse kommunizieren über Nachrichten. SPMD: Sequenzielles Programm, und Daten Partitionierung
- Benutzung
- Programmierung
- Definition MPI_Init
- Definition MPI_Finalize
- Definition MPI_Comm_Size:
- Definition MPI_Comm_rank
- Definition MPI_Get_processor_name
- Definition MPI_Send
- Definition MPI_Recv
- MPI Datentypen
Benutzung:
Aufruf:
Ein MPI Programm kann mit dem Befehl mpiexec aufgerufen werden.
Beispiel:
$ mpiexec -n 3 myProgramm arg1 arg2
SLURM:
Um ein MPI Programm mit SLURM zu starten benutzt man den Befehl srun.
Beispiel:
$ surn -N 3 myProgramm
Option
|
Funktion
|
--partition, -p |
Definiert die Partition auf welcher der Job laufen soll. Wenn dies nicht angegeben wird, wird defq verwendet. |
--nodes, -N |
Definiert die Anzahl der Knoten, auf denen der Job laufen soll. |
--ntasks, -n |
Definiert die Anzahl der Tasks für den Job. |
--ntasks-per-node |
Definiert die maximale Anzahl der Tasks pro Knoten. Wird in der Regel mit MPI Programmen benutzt. |
--cpus-per-task |
Definiert die Anzahl der Rechenkerne pro Task. In der Regel bei Verwendung von OpenMP wichtig. |
--mem |
Definiert das Arbeitsspeicher-Limit pro Knoten. Der Job wird abgebrochen, sollte das Limit überschritten werden. Der Zahlenwert ist in Megabyte. |
--time, -t |
Definiert das Zeitlimit des Jobs. Wird das Limit überschritten, wird der Job abgebrochen. Format: D-HH:MM:SS |
--output=<Dateiname> |
Beim sbatch-Befehl spezifiziert dies die Log-Datei in welcher der stdout-Stream geleitet wird. Standardmäßig wird im Ordner, in dem sbatch ausgeführt wurde, eine Datei namens slurm-<JobID>.out angelegt. |
--error=<Dateiname> |
Beim sbatch-Befehl spezifiziert dies die Log-Datei, in die der stdout-Stream geleitet wird. Standardmäßig wird im Ordner, in dem sbatch ausgeführt wurde, eine Datei namens slurm-<JobID>.out angelegt. |
--mail-type |
Spezifiziert die Ereignisse, bei denen eine E-Mail an die mit --mail-user spezifizierte Adresse versendet werden soll. Mögliche Angaben sind BEGIN, END, FAIL, REQUEUE, ALL. |
--mail-user=<Adresse> |
Spezifiziert den Empfänger der E-mail. |
Man kann ein MPI Programm auch über ein Bash-Script starten. Dazu wird der Befehl sbatch verwendet.
Beispiel:
$ sbatch beispiel.sh
Im Bash-Script lassen sich die SLURM Parameter über #SBATCH setzten.
Beispiel Scrpit:
#!/bin/bash
#SBATCH –time=0:20:00
#SBATCH –nodes=3
#SBATCH –tasks-per-node=1
#SBATCH --mem 48000
#SBATCH –partition=short
module load openmpi/gcc/64/2.1.2
echo "Number of tasks: " echo $SLURM_NTASKS
mpirun -np $SLURM_NTASKS ~/myProgramm
# Alternativ geht im Scrpit auch:
# srun -N 3 ~/myProgramm
Programmierung:
Benutzte Librarry:
#include <mpi.h>
MPI-1.2 hat 129 Routinen (und MPI-2 hat sogar noch mehr ...)
- Oft reichen schon 6 Routinen um ein MPI Programm zu schreiben:
- MPI_Init – MPI initialisieren
- MPI_Finalize – MPI aufräumen
- MPI_Comm_size – Gibt die Anzahl der Prozesse zurück
- MPI_Comm_rank – Gibt die eigene Prozessnummer zurück
- MPI_Send – Sende einen Nachricht
- MPI_Recv – Empfange eine Nachricht
MPI initialisieren und beenden:
Definition: MPI_Init
Jeder MPI Prozess muss MPI_Init aufrufen, bevor er andere MPI Routinen nutzen kann.
int MPI_Init(int *argc, char
***argv)
|
||
INOUT
|
argc | Pointer to argc of main() |
INOUT
|
argv | Pointer to argv of main() |
Result
|
MPI_SUCCESS or error code |
Definition: MPI_Finalize
Jeder MPI Prozess muss am ende MPI_Finalize aufrufen. MPI_Finalize dient dem freigeben von Ressourcen. Danach dürfen keine weiteren MPI Routinen mehr benutzt werden.
Achtung MPI_Finalize Terminiert nicht den Prozess.
int MPI_Finalize |
|
Result |
MPI_SUCCESS or error code |
Beispiel:
int main(int argc, char **argv){
MPI_Init(&argc, &argv); //Aufruf von MPI_Init mit Übergabe der Kommandozeilen Parameter.
… //In diesem Abschnitt können MPI Routinen aufgerufen werden.
MPI_Finalize; //Deallokieren der Ressourcen.
}
Definition MPI_Comm_Size:
Gibt die Anzahl der Prozesse zurück.
int MPI_Comm_size(MPI_Comm comm, int *size) |
||
In |
comm |
Communicator |
OUT |
size |
Number of processes in comm |
Beispiel:
MPI_Comm_size(MPI_COMM_WORLD, &nprocs) //gibt die Anzahl an MPI Prozessen in nprocs zurück
Definition MPI_Comm_rank:
Gitb die Prozessnummer zurück. Die Prozessnummernirrung startet bei 0 und zählt aufwerts.
int int MPI_Comm_rank(MPI_Comm comm, int *rank) |
||
In |
comm |
Communicator |
OUT |
rank |
Number of processes in comm |
Zu Kommunikatoren:
Kommunikatoren sind Gruppen von Prozessen. In der vordefinierten Gruppe MIP_COMM_WORLD sind alle Prozesse der parallelen Anwendung enthalten. Neue Kommunikatoren könne bei bedarf erzeugt werden. (dazu später mehr).
Definition MPI_Get_processor_name:
Gibt den Namen des Knotens sowie die Länge des Namens als Integer an.
int int MPI_Get_processor(char *name, int *resultlen) |
||
OUT |
name |
A unique specifiert for the physical node |
OUT |
resultlen |
Length of name must be array of length [MPI_MAX_PROCESSOR_NAME] |
Senden und empfangen mit MPI_Send und MPI_Recv:
MPI_Send und MPI_Recv sind beides blockierende Operationen d.h. der Prozess wartet bis die Nachricht komplett von dem Sendepuffer gelesen wurde bzw. komplett in den Empfangspuffer geschrieben wurde.
Definition MPI_Send:
int MPI_Send(void *buf, int count, MPI_Datatype dtype, int dest, int tag, MPI_Comm comm) |
||
IN |
buf |
(Pointer to) the data to be sent (send buffer) |
IN |
count |
Number of data elements (of type dtype ) |
IN |
dtype |
Data type of the individual data elements |
IN |
dest |
Rank of destination process in communicator comm |
IN |
tag |
Message tag |
IN |
comm |
Communicator |
Definition MPI_Recv:
int MPI_Recv(void *buf, int count, MPI_Datatype dtype, int source, int tag, MPI_Comm comm, MPI_Status *status) |
||
OUT |
buf |
(Pointer to) receive buffer |
IN |
count |
Buffer size (number of data elements of type dtype) |
IN |
dtype |
Data type of the individual data elements |
IN |
source |
Rank of source process in communicator comm |
IN |
tag |
Message tag |
IN |
comm |
Communicator |
OUT |
status |
Status (among others: actual message length) |
Damit ein Prozess einen Nachricht empfangen kann müssen sowohl der Parameter Tag als auch der Kommunikator bei Sender und Empfänger übereinstimmen. Des weiteren muss der Parameter Source beim Empfänger mit dem Parameter Dest des Sender übereinstimmen. Für Tag und Sender können auch folgende wild-cards verwendet werden:
MPI_ANY_SOURCE MPI_ANY_TAG
Des weiteren darf die Nachricht nicht größer sein als die angegeben Puffergröße sein.
MPI Datentypen:
MPI Datentypen sind:
MPI |
C |
MPI_CHAR |
char |
MPI_SHORT |
short |
MPI_INT |
int |
MPI_LONG |
long |
MPI_FLOAT |
float |
MPI_DOUBLE |
double |
MPI_BYTE |
Byte with 8 bits |
MPI_UNSIGNED_CHAR |
unsigned char |
MPI_UNSIGNED_SHORT |
unsigned short |
MPI_UNSIGNED |
unsigned int |
MPI_UNSIGNED_LONG |
unsigned long |
MPI_LONG_DOUBLE |
long double |
MPI_PACKED |
Packed Data |