Lehrbrief ist erlaubt!
include MPI header file variable declarations initialize the MPI environment ...do computation and MPI communication calls... close MPI communications
MPI und haben
int als Rückgabewert (sollte
MPISUCCESS
sein)MPICHAR,
MPISHORT
, MPIINT,
MPILONG
, MPIUNSIGNEDCHAR
, MPIUNSIGNEDSHORT
, MPIUNSIGNED,
MPIUNSIGNEDLONG,
MPIFLOAT
, MPIDOUBLE,
MPILONGDOUBLE,
MPIBYTE
, MPIPACKED
* Spezielle Datentypen:
MPICOMM
, MPISTATUS,
MPIDATATYPE
int err; err = MPI_Init(&argc, &argv);
MPICOMMWORLD
), ihren Rang in einem Communicator erhalten sie mit int MPI_Comm_rank(MPI_Comm comm, int *rank);
int MPI_Comm_size(MPI_Comm comm, int *size);
err = MPI_Finalize();
#include <stdio.h> #include <mpi.h> void main (int argc, char *argv[]) { int myrank, size; MPI_Init(&argc, &argv); // Initialize MPI MPI_Comm_rank(MPI_COMM_WORLD, &myrank); // Get my rank MPI_Comm_size(MPI_COMM_WORLD, &size); // Get the total number of processors printf("Processor %d of %d: Hello World!\n", myrank, size); MPI_Finalize(); // Terminate MPI }
int MPI_Send(void *buf, int count, MPI_Datatype dtype, int dest, int tag, MPI_Comm comm);
int MPI_Recv(void *buf, int count, MPI_Datatype dtype, int source, int tag, MPI_Comm comm, MPI_Status *status);
source
, tag
und communicator
müssen den Werten aus MPISend entsprechen (Wildcards sind erlaubt für
source und
tag)
*
buffer und
status sind Output-Parameter, der Rest Input
* Sender und Empfänger müssen denselben Datentyp verwenden, sonst kann es zu unvorhergesehenen Ergebnissen kommen
* wenn der Puffer länger ist als angegeben, kommt es zu einem Fehler
* Wildcards beim Empfangen
*
MPIANYSOURCE und
MPIANYTAG sind die Wildcards
* über
status.MPISOURCE
und status.MPITAG können die konkreten Werte ermittelt werden
* tatsächliche Anzahl an Elementen in der empfangenen Nachricht ermitteln (
count ist lediglich das Maximum der möglichen Werte): <code>int MPIGetcount(MPIStatus *status, MPIDatatype dtype, int *count);</code>
* Beim Senden können zwei unterschiedliche Dinge passieren
* die Nachricht wird in einen MPI-Puffer kopiert und im Hintergrund verschickt
* die Nachricht bleibt in den Programmvariablen bis der empfangende Prozess bereit zum Empfangen ist
* wenn
MPISEND
zurückkehrt heißt das nicht, dass die Nachricht angekommen ist, sondern nur, dass sie MPI übergeben wurdeint MPI_Isend(void *buf, int count, MPI_Datatype dtype, int dest, int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Irecv(void *buf, int count, MPI_Datatype dtype, int source, int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Wait( MPI_Request *request, MPI_Status *status );
int MPI_Test( MPI_Request *request, int *flag, MPI_Status *status );
MPI_IRECV(...,request) ... arrived=FALSE while (arrived == FALSE) { "work planned for processor to do while waiting for message data" MPI_TEST(request,arrived,status) } "work planned for processor to do with the message data"
MPIBUFFERATTACH
und MPIBUFFERDETACH
)for (i=0; i<n; ++i) { MPI_Send(&a[k+i][l], m, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD); }
p = &buffer; for (i=k; i<k+n; ++i) { for(j=l; j<l+m; ++j) { *(p++) = a[i][j]; } } MPI_Send(p, n*m, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD)
MPIPACK verwendet werden, das genau diese Aufgabe übernimmt
* <code>count = 0;
for(i=0; i<n; i++){
MPIPack(&a[k+i][l], m, MPIDOUBLE, buffer, bufsize, count, MPICOMMWORLD);
}
MPISend(buffer, count, MPIPACKED, dest, tag, MPICOMMWORLD);</code>
*
MPIUNPACK
muss dann anstatt MPIRECV zum Empfangen verwendet werden (mit
MPIPACKSIZE kann die hierfür benötigte Größe des Puffers ermittelt werden)
* an den Nachrichten selbst kann man nicht erkennen, ob
MPIPACK
verwendet wurde, man könnte sie also ganz "normal" z.B. als Double empfangenint MPIBarrier ( MPIComm comm )
synchronisiert Prozesse ohne Daten zu übertragen; evtl. Overhead, daher sparsam verwendenint MPIBcast ( void* buffer, int count, MPIDatatype datatype, int rank, MPIComm comm ) schickt Daten vom Rootprozess an alle anderen
* <code>#include <mpi.h>
void main(int argc, char argv[]) {
int rank;
double param;
MPIInit(&argc, &argv);
MPICommrank(MPICOMMWORLD,&rank);
if(rank==5) param=23.0;
MPIBcast(¶m,1,MPIDOUBLE,5,MPICOMMWORLD);
printf("P:%d after broadcast parameter is %f \n",rank,param);
MPIFinalize();
}</code>
* int MPIReduce ( void* sendbuffer, void* recvbuffer, int count, MPIDatatype datatype, MPIOp operation, int rank, MPIComm comm )
: sammelt Daten von den Prozessen ein, reduziert diese Daten auf einen Wert, speichert den reduzierten Wert im Rootprozess
*
* count
, datatype
, rank
müssen in allen Prozessen gleich sein
* operation
wird auf den Werten durchgeführt (sum, min, max etc.)
* <code>#include <stdio.h>
#include <mpi.h>
void main(int argc, char *argv[])
{
int rank;
int source,result,root;
/ run on 10 processors /
MPIInit(&argc, &argv);
MPICommrank(MPICOMMWORLD,&rank);
root=7;
source=rank+1;
MPIBarrier(MPICOMMWORLD);
MPIReduce(&source,&result,1,MPIINT,MPIPROD,root,MPICOMMWORLD);
if(rank==root) printf("P:%d MPIPROD result is %d \n",rank,result);
MPIFinalize();
}</code>
* int MPIGather ( void* sendbuffer, int sendcount, MPIdatatype sendtype, void* recvbuffer, int recvcount, MPIDatatype recvtype, int rank, MPIComm comm )
sammelt Daten von allen Prozessen im Rootprozess
*
* wie Send in jedem Prozess und n * Recv im Root
* <code>#include <stdio.h>
#include <mpi.h>
void main(int argc, char argv[])
{
int rank,size;
double param[16],mine;
int sndcnt,rcvcnt;
int i;
MPIInit(&argc, &argv);
MPICommrank(MPICOMMWORLD,&rank);
MPICommsize(MPICOMMWORLD,&size);
sndcnt=1;
mine=23.0+rank;
if(rank==7) rcvcnt=1;
MPIGather(&mine,sndcnt,MPIDOUBLE,param,rcvcnt,MPIDOUBLE,7,MPICOMMWORLD);
if(rank==7)
for(i=0;i<size;++i) printf("PE:%d param[%d] is %f \n",rank,i,param[i]]);
MPIFinalize();
}</code>
* MPIALLGATHER macht das gleiche wie
MPIGATHER
und anschließend direkt ein MPIBCAST → Daten werden direkt an alle Prozesse verteilt
*
int MPIScatter ( void* sendbuffer, int sendcount, MPIdatatype sendtype, void* recvbuffer, int recvcount, MPIDatatype recvtype, int rank, MPIComm comm ) sendet Daten vom Rootprozess an alle Prozesse abhängig vom Rank
*
* wie n * Send im Root und Recv in jedem Prozess
* <code>#include <stdio.h>
#include <mpi.h>
void main(int argc, char *argv[]) {
int rank,size,i;
double param[8],mine;
int sndcnt,rcvcnt;
MPIInit(&argc, &argv);
MPICommrank(MPICOMMWORLD,&rank);
MPICommsize(MPICOMMWORLD,&size);
rcvcnt=1;
if(rank==3) {
for(i=0;i<8;++i) param[i]=23.0+i;
sndcnt=1;
}
MPIScatter(param,sndcnt,MPIDOUBLE,&mine,rcvcnt,MPIDOUBLE,3,MPICOMMWORLD);
for(i=0;i<size;++i) {
if(rank==i) printf("P:%d mine is %f n",rank,mine);
fflush(stdout);
MPIBarrier(MPICOMMWORLD);
}
MPIFinalize();
}</code>
* MPIALLREDUCE — used to combine the elements of each process's input buffer and stores the combined value on the receive buffer of all group members.
* User-Defined Reduction Operations — enable reduction to be defined as an arbitrary operation.
* Gather / Scatter Vector Operations — MPIGATHERV and MPISCATTERV allow a varying count of data from/to each process.
* Other Gather / Scatter Variations — MPIALLGATHER and MPIALLTOALL
* No root process specified: all processes get gathered or scattered data.
* Send and receive arguments are meaningful to all processes.
* MPISCAN — used to carry out a prefix reduction on data throughout the group and returns the reduction of the values of all of the processes.
* MPIREDUCESCATTER combines an MPIREDUCE and an MPISCATTERV.
==== Kommunikatoren ====
* Kommunikatoren fassen Prozesse zusammen und ermöglichen ihnen die Kommunikation miteinander
*
MPICOMMWORLD enthält alle Prozesse
* es gibt Intrakommunikatoren (Gruppe von Prozessen, die in dieser einen eindeutigen Rang haben) und Interkommunikatoren (Kommunikation zwischen Intrakommunikatoren)
* Erzeugen von Intrakommunikatoren
* MPI-1: Aufspalten (
MPICommsplit), duplizieren (
MPICommdup) vorhandener Intrakommunikatoren, erzeugen einer neuen Gruppe aus Prozessen, neu anordnen von Prozessen einer Gruppe
* MPI-2: verbinden zweier Anwendungen und mergen ihrer Prozesse, neue Prozesse erzeugen
*
*
MPICommsplit(MPIComm comm, int color, int key, MPIComm *newcomm) spaltet einen Kommunikator in Subkommunikatoren
* Reihenfolge nach
key, wenn identisch nach Rang in
comm
*
MPICommgroup(MPIComm comm, MPIGroup *group) extrahiert das Handle einer Prozessgruppe aus einem Kommunikator
*
MPIGroupincl(MPIGroup group, int n, int *rank, MPIGroup *newgroup) erzeugt eine neue Gruppe aus den in
rank angegebenen Prozessen
* werden in
rank Prozesse angegeben, die nicht in der Gruppe
group sind, bricht MPI mit einem Fehler ab, ebenso bei doppelten Einträgen
* wenn
n 0 ist, ist die neue Gruppe
MPIEMPTYGROUP
*
MPIGroupexcl hat die gleiche Syntax und erzeugt eine Gruppe mit den Prozessen außer den in
rank angegebenen
* die Reihenfolge der Ranks in
rank ist unerheblich für die neuen Ranks in
newgroup
* wenn
n 0 ist, ist die neue Gruppe gleich der alten
*
MPIGroupunion(MPIGroup group1, MPIGroup group2, MPIGroup *newgroup)
, MPIGroupintersection
und MPIGroupdifference
erzeugt zwei Gruppen eine neue durch Anwendung der entsprechenden Mengenoperation
* Abfragen der Informationen der Gruppen
* MPIGroupsize(MPIGroup group, int *size)
*
MPIGrouprank(MPIGroup group, int *rank)
* MPIUNDEFINED wenn aufrufender Prozess nicht zur Gruppe gehört
*
MPIgrouptranslateranks(MPIGroup group1, int n, int *rank1, MPIGroup group2, int *rank2)
* MPIGroupcompare(MPIGroup group1, MPIGroup group2, int *result)
* result
: MPIIDENT,
MPISIMILAR
oder MPIUNEQUAL
*
MPIGroupfree(MPIGroup *group)
zerstört eine Gruppe
* MPICommcreate(MPIComm comm, MPIGroup group, MPIComm *newcomm) erzeugt einen neuen Kommunikator auf Basis einer Gruppe
* muss von allen betroffenen Prozessen aufgerufen werden
* Prozesse, die nicht in der Gruppe sind, erhalten
MPICOMMNULL als Rückgabewert
*
MPICommfree(MPIComm *comm)
zerstört einen Kommunikator
===== ToDo =====
* Online-Learning anschauen
* externe Quellen suchen
* Gesetze Amdahl etc.
* MPI-Standard
* Architekturen verstehen (Shared Memory etc.)
* Metriken verstehen, Metriken (Formeln) für neue Topologie entwickeln
* Leistungsbewertung (Gesetze Amdahl etc.)
* OpenMP eher allgemein (Kombination mit MPI)
* OpenMP-Webcast
* Bibliotheken für Parallelrechner nur oberflächlich
* Lehrbrief korrigieren (falsche Parameter bei MPI-Funktionen)