se:parallelrechner
Parallelrechner
Klausur
Codeabschnitte verifizieren
Online-Learning anschauen
Programm als Lückentext
Verstehen der wichtigsten Funktionen (send/recv)
Architekturen verstehen (Shared Memory etc.)
* Metriken verstehen, Metriken (Formeln) für neue Topologie entwickeln
Leistungsbewertung (Gesetze Amdahl etc.)
OpenMP eher allgemein (Kombination mit MPI)
Bibliotheken für Parallelrechner nur oberflächlich
* Leseempfehlung
Gesetze Amdahl etc.
MPI-Standard
NICHT
virtuellen Topologien
Matrizenrechnung
Lehrbrief ist erlaubt!
Einstieg in Parallelrechner
Arten von Parallelrechnern
Symmetrische Multiprozessorsysteme (SMP): übliche Rechner mit mehreren Prozessoren (auch die heutigen MultiCore-PCs)
Rechner-Cluster: übliche Rechnerknoten verbunden über ein überdurchschnittlich schnelles Netzwerk, keine direkte Eingabemöglichkeit sondern Zugang über Eingangsknoten
Vernetzte Workstations (NOW): übliche Workstations, die einen ad-hoc Paralllelrechner mit begrenzter Leistung bilden
Grids: Internet-basierter Zusammenschluss verteilter Rechner (z.B. SETI)
die meisten Parallelrechner laufen unter Linux und verwenden Standard-APIs für die Programmierung (MPI, OpenMP, ScaLAPACK)
Architekturen und Klassifizierungen von Parallelrechnern
Statische Verbindungsnetze
-
Verbindungsstrukturen bestehen aus festen Links zwischen Rechnern
regelmäßige Strukturen werden bevorzugt
einfacheres Routing
Symmetrie → leichter verständlich, leichtere Entwicklung, Prozessoren lassen sich leichter tauschen
Modularität → leichter erweiterbar und umformbar
Metriken
Distanz zwischen zwei Knoten: das Minimum aller Pfadlängen zwischen dem betrachteten Knotenpaar
Durchmesser: die größtmögliche Distanz in einem Graphen
Halbierungsbreite: Mindestanzahl der Kanten, die entfernt werden müssen, damit ein Graph in zwei gleichgroße Hälften zerfällt
typische Netzwerke
Skalierbarkeit und Emulation
Was ist das kleinste Netzinkrement, das hinzugefügt werden muss, um das nächstgrößere Netz der gleichen Topologie zu erhalten?
Wie ist die Abhängigkeit der Effizienz einer Topologie von der Knotenzahl?
Emulation: Abbildung eines Gastgraphen G auf einen Wirtsgrafen H, bei der alle Knoten von G auf Knoten von H zum Liegen kommen. Jede Kante von G wird auf eine oder mehrere Kanten von H abgebildet.
* Beispiele: Ring im 2D-Torus, Baum im Gitter, Gitter im Hyperkubus
* Verlangsamung durch Emulation
* Knotenlast: die größte Zahl der Gastknoten, die auf einem Wirtsknoten zu liegen kommen
* Dilatation: die Länge des längsten Pfades in H, auf den eine beliebige Kante von G abgebildet wird
* Andrang/Kantenlast: die größte Zahl der Kanten von G, die auf dieselbe Kante in H abgebildet werden
* Beispiele für Dilatation 1: Gitter -> Hyperkubus, Ring -> Gitter
Dynamische Verbindungsnetzwerke
Verbindungsarten
Leitungsvermittlung: eine physikalische Verbindung wird für die Dauer der gesamten Übertragung zugewiesen
Paketvermittlung: ein Paket wird an das VN abgegeben und von diesem über Zwischenknoten an den Zielknoten übertragen
Routing-Strategien
Store-and-Forward: jeder Zwischenknoten speichert die Nachricht vollständig, bevor sie dem nächsten Knoten übergeben wird (Übertragungszeit ist proportional zur Knotenzahl)
Cut-Through: Nachrichten werden in Teile gespalten und sofort weitergeschickt (Knoten müssen gleichzeitig senden und empfangen können)
Wormhole: Nachricht wird in kleine Teile (Flits, 1-2 Byte) zerteilt und zunächst wird nur der Kopf der Nachricht verschickt, wenn dieser den Knoten verlässt, wird der Rest der Nachricht angefordert
Vergleich
Latenz wächst bei SaF mit Knotenzahl, dafür wird Netz nicht belastet, da Knoten sofort wieder zur Verfügung stehen
beim Wormhole werden die Knoten lange blockiert und es kann zu Deadlocks kommen, allerdings bleibt die Latenz mit steigender Knotenzahl konstant
* typische dynamische Verbindungsnetze
Merkmale
Bandbreite (Bits/s)
Latenzzeit (Verzögerung erster Bit zwischen Sender und Empfänger)
Fehlertoleranz
Skalierbarkeit
einstufig: Bus, Crossbar, Shuffle
mehrstufig: Banyan, Benes, CLOS
* Bus: höchstens 1 Eingang wird zu allen Ausgängen durchgeschaltet -> sehr verbreitet aber auf kleine Teilnehmerzahl beschränkt
* Crossbar: ermöglicht gleichzeitige Kommunikation zwischen verschiedenen Teilnehmerpaaren
* Shuffle: vereinfachter Crossbar mit eingeschränkten Steuereingängen
* Shuffle mit Broadcast-Erweiterung: zusätzliches Broadcast-Bit zur Erweiterung der Schaltfunktion (1 Eingang -> 2 Ausgänge)
* Banyan: Shuffle in Grundschaltung in mehreren Ebenen, Blockierungen sind möglich
* CLOS: besteht aus Crossbars
Grundbegriffe der Leistungsbewertung
Tcomp Rechenzeit, Tcomm Kommunikationszeit, Tser Ausführungszeit der seriellen Programmversion, Tpar Ausführungszeit der parallelen Programmversion, n Anzahl Knoten
Granularität: Länge der seriellen Codeabschnitte zwischen Kommunikations- und Synchronisationsbefehlen
computation / communication ratio: Tcomp / Tcomm
Beschleunigung: S(n) = Tser / Tpar
Effizienz: S(n) / n
Kosten: n * Tpar
* Gesetz von Amdahl: S(n) = (s + p) / (s + p/n)
geht von einem konstanten seriellen Anteil im Programm aus → Grenze für Speedup, nicht-skalierbare Anwendungen
Die Verbesserung der Leistung des Gesamtsystems durch Verbesserung einer Komponente ist limitiert durch den Umfang der Verwendung der Komponente.
Gesetz von Gustavson-Barsis: S
s(n) = n + (1 - n) s
* geht von einer konstanten Laufzeit des seriellen Anteils im Programm aus -> skalierbare Anwendungen, können bei wachsendem Problem mit mehr Prozessoren ausgestattet werden
* Verbesserung der Genauigkeit/Problemgröße eines parallelen Verfahrens durch Steigerung der Prozessorzahl bei gleich bleibender Ausführungszeit
Nachrichtenaustausch
Nachrichten werden zwischen den Prozessen verschickt, indem der Absender diese in einen Umschlag mit Empfänger und Sender verpackt und ggfs. mit einem Tag versieht
Prozesserzeugung
ein Prozess versendet eine Nachricht, indem er
ein Prozess empfängt eine Nachricht, indem er
den Absender nennt (exakt oder beliebiger Absender)
den Typ der Daten und die Zieladresse nennt
optional einen Tag angibt (z.B. "dringend")
synchroner/blockierender Nachrichtenaustausch
erst Send oder erst Receive möglich
Prozesse warten, bis die Nachricht übertragen wurde (und blockieren solange)
asynchroner/nicht blockierender Nachrichtenaustausch
* Empfänger kann ein IRecv absetzen und später mit Wait schauen, ob die Nachricht angekommen ist
Online-Kurs
Einstieg in parallele Programmierung
Voraussetzungen für effektive Parallelisierung
schnelle Verbindung zwischen Prozessoren und Speicher und den einzelnen Prozessen, sowie schnelle Datenübertragung in und aus dem Speicher
Protokoll für Interprozesskommunikation
die Algorithmen müssen parallelisierbar sein und in kleine Teilprobleme aufgeteilt werden können
Mechanismus zur Verteilung der Aufgaben an die Prozesse
Computerarchitekturen nach Flynn 1972
Single Instruction Single Data (SISD)
Multiple Instruction Single Data (MISD)
Single Instruction Multiple Data (SIMD)
1 CPU zur Steuerung und mehrere CPUs mit eigenem Speicher
Steuer-CPU sendet Broadcasts und die anderen CPU rechnen, abhängig von konditionalen Bedingungen im Code
Nachteil: viele CPUs bleiben idle
Multiple Instruction Multiple Data (MIMD)
jede CPU hat übernimmt sowohl Steuerung als auch Berechnung
Programme werden von jeder CPU unabhängig von den anderen ausgeführt → asynchron
3 Typen: shared memory (CPUs teilen sich gemeinsamen Speicher), distributed memory (Knoten, die zusammen ein Problem lösen) und SMP (Kombination der beiden vorherigen)
Shared-Memory MIMD
Verbindung zwischen CPUs und Speicher via Bus oder Switch
-
CPUs haben zusätzlich internen Speicher: Register und Cache
Problem bei Verwendung von Cache: Variablen haben nach Änderung durch anderen Prozess vielleicht falschen Wert → Protokoll wird benötigt zum Ermitteln solcher Fälle
Distributed-Memory MIMD
jede CPU hat eigenen Speicher → Menge von Knoten ergeben den gesamten Parallelrechner
-
da kein Zugriff auf den Speicher der anderen Knoten besteht, müssen geeignete Programmiertechniken verwendet werden, um den Prozessen den Zugriff zu ermöglichen
SMP Cluster
Symmetric Multi-Processing
Netzwerk (→ distributed) aus shared memory Clustern
Beispiel: Earth Simulator
Modelle paralleler Programmierung
Design paralleler Programme
parallele Programme bestehen aus mehreren Instanzen serieller Programme, die über Bibliotheksfunktionen kommunizieren, die sich wie folgt einteilen lassen:
initialize, manage, terminate → Start der Kommunikation, Anzahl der Prozesse ermitteln, Subgroups erstellen
point-to-point: send/receive zwischen Prozesspaaren
Kommunikation zwischen Prozessgruppen → Synchronisation, verteilte Berechnung
Erstellen von Datentypen
Dekomposition des Problems
Load Balancing
Arbeit wird gleichmäßig auf die Prozesse verteilt, damit keine idle sind
einfach, wenn die gleichen Aufgaben auf mehreren Datenbereichen durchzuführen sind
Ausführungszeit
Prozesswartezeit verringern
Einstieg in MPI
Standard-Implementierung für message passing, keine Bibliothek sonder nur
API-Spezifikation
MPI-Programme bestehen aus mindestens zwei autonomen Prozessen, die ihren eigenen Code ausführen
die Prozesse kommunizieren über MPI-Funktionen und werden über ihren Rang identifiziert
die Anzahl der Prozesse ist nicht zur Laufzeit änderbar, sondern wird beim Aufruf des Programms festgelegt
MPI-1 wurde 1994 entwickelt vom MPI-Forum, dessen Mitglieder aus 60 Organisationen stammen
der Standard definiert Namen, Aufrufsequenzen und Rückgabewerte von Funktionen → Interface
die Implementierung ist dem jeweiligen Hersteller überlassen → Optimierung für Plattformen möglich
MPI ist für viele Plattformen verfügbar
MPI-2 wurde bereits definiert, ist aber noch nicht auf allen Plattform verfügbar (Features: paralleles I/O, C++-Bindings etc.)
Ziele von MPI
von MPI wird nicht definiert/angeboten
MPI kann verwendet werden, wenn
MPI sollte nicht verwendet werden, wenn
Schleifen-Parallelisierung ausreicht
es bereits parallele Bibliothken für den Fachbereich gibt (z.B. mathematische Bibliotheken)
man überhaupt keine Parallelisierung benötigt
Typen von MPI-Routinen
point-to-point communication: 1-zu-1-Kommunikation
collective communication: 1-zu-viele-Kommunikation, Synchronisierung
process groups
Process topologies
Environment management and inquiry
point-to-point communication
elementare Kommunikation zwischen 2 Prozessen: send/receive, beide Prozesse müssen aktiv handeln
Kommunikation per Nachrichten mit Envelope (source, destination, tag etc.) und Body (Daten)
Teile des Nachrichten-Bodys: buffer (Speicherplatz der Daten), datatype (primitive oder eigene Datentypen), count (Anzahl der Datentypen)
MPI definiert eigene Datentypen um unabhängig von der Implementierung z.B. der Gleitkommazahlen auf den unterschiedlichen Plattformen zu bleiben
Modi des Nachrichtenversands
standard
synchron: Senden ist erst nach Bestätigung des Empfängers abgeschlossen
buffered: Senden ist abgeschlossen, nachdem die Daten in den lokalen Puffer kopiert wurden
ready
erfolgreiches Senden bedeutet, dass der ursprüngliche Speicherplatz der Daten überschrieben werden kann
Receive ist beendet, wenn die Daten tatsächlich angekommen sind
blockierende Kommunikation: die send-/receive-Routine kehrt erst zurück, wenn die Daten tatsächlich versendet wurden
nicht-blockierende Kommunikation: die send-/receive-Routine kehrt sofort nach dem Aufruf zurück ohne sicherzustellen, dass die Daten tatsächlich versendet wurden, der Prozess kann andere Aufgaben übernehmen und später prüfen, ob die Daten angekommen sind
collective communications
ein communicator ist eine Gruppe von Prozessen, die miteinander kommunizieren dürfen, alle Prozesse gehören stets zu MPICOMMWORLD
collective operations übertragen Daten zwischen allen Prozessen einer Gruppe
Synchronisation: alle Prozesse warten, bis sie einen bestimmten Punkt erreicht haben
Datenbewegung: Daten werden an alle Prozesse verteilt
kollektive Berechnung: ein Prozess einer Gruppe sammelt Daten von anderen Prozessen ein und führt Operationen auf den Daten durch
Vorteile der collective operations im Gegensatz zu point-to-point
Broadcast: ein Prozess sendet Daten an alle Prozesse seiner Gruppe
Scatter und Gather: Verteilen und Einsammeln von Daten zwischen Prozessen
Reduktion: ein Root-Prozess sammelt Daten von mehreren Prozessen ein und berechnet einen Einzelwert
Prozessgruppe: geordnete Gruppe von Prozessen, die jeweils einen Rang (=ID) haben, Prozesse können Mitglied mehrerer Gruppen sein
Prozesstopologien: Anordnung von Prozessen in geometrischen Figuren (Grid oder Graph), rein virtuelle Anordnung unabhängig von physikalischer Anordnung der Prozessoren, ermöglichen effiziente Kommunikation und erleichtern die Programmierung
Management und Abfragen der Umgebung: initialisieren und beenden von Prozessen, Rangermittlung
MPI Programmstruktur
grundsätzlicher Aufbau von MPI-Programmen
include MPI header file
variable declarations
initialize the MPI environment
...do computation and MPI communication calls...
close MPI communications
MPI-Funktionsnamen beginnen immer mit MPI und haben
int als Rückgabewert (sollte
MPISUCCESS
sein)
-
MPI-Datentypen einer Send-/Receive-Kombination müssen übereinstimmen
MPI-Standarddatentypen: MPICHAR,
MPISHORT
, MPIINT,
MPILONG
, MPIUNSIGNEDCHAR
, MPIUNSIGNEDSHORT
, MPIUNSIGNED,
MPIUNSIGNEDLONG,
MPIFLOAT
, MPIDOUBLE,
MPILONGDOUBLE,
MPIBYTE
, MPIPACKED
* Spezielle Datentypen:
MPICOMM
, MPISTATUS,
MPIDATATYPE
Initialisierung von MPI:
int err;
err = MPI_Init(&argc, &argv);
Prozesse kommunizieren über Communicator miteinander (z.B.
MPICOMMWORLD
), ihren Rang in einem Communicator erhalten sie mit
int MPI_Comm_rank(MPI_Comm comm, int *rank);
Die Anzahl der Prozesse in einem Communicator ermittelt
int MPI_Comm_size(MPI_Comm comm, int *size);
MPI wird beendet mit
err = MPI_Finalize();
HelloWorld mit MPI:
#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
}
Point-to-point Kommunikation
Punkt-zu-Punkt-Verbindungen stellen die fundamentale Kommunikation zwischen Prozessen dar
Probleme: welche Nachricht wird verarbeitet, wenn mehrere empfangen werden können; synchrone/asynchrone Kommunikation
beide Teilnehmer (Sender und Empfänger müssen aktiv partizipieren)
Sender und Empfänger arbeiten meist asynchron (Sender sendet z.B. erst nachdem der Empfänger schon empfangen will)
Nachrichten bestehen aus
Envelope: Source, Destination, Communicator, Tag
Body: Buffer, Datatype, Count
verschickte, noch nicht empfangene Nachrichten hängen in der pending queue, aus der die empfangenden Prozesse die nächsten Nachrichten auswählen können (nicht nur simple FIFO-Queue)
Nachricht blockierend versenden:
int MPI_Send(void *buf, int count, MPI_Datatype dtype, int dest, int tag, MPI_Comm comm);
alle Parameter sind Input-Parameter
* Nachricht blockierend empfangen:
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 wurde
bei der Kommunikation muss man darauf achten, Deadlocks zu verhindern
Kommunikation genau planen (z.B. P1 send dann recv, P2 recv dann send)
auch P1 send dann recv, P2 send dann recv kann zu einem Deadlock führen, wenn die Nachrichten zu groß für den MPI-Puffer sind
blockierende und nicht-blockierende Kommunikation können gemischt werden (sogar bei derselben Nachricht)
nicht-blockierende Kommunikation benötigt zwei Aufrufe: posting eines sends und eines receives
die Aufrufe werden beendet entweder durch "Nachfragen" des Prozesses oder durch Warten des Prozesses
die postings werdne über ein Request-Handle identifiziert, dass der aufrufende Prozess nutzen kann um den Status abzufragen
nicht-blockierendes Senden:
int MPI_Isend(void *buf, int count, MPI_Datatype dtype, int dest, int tag, MPI_Comm comm, MPI_Request *request);
das I steht für Initiate
der Aufruf startet lediglich das Senden, es muss ein zusätzlicher Aufruf erfolgen, um den Vorgang abzuschließen
die Parameter sollten nicht gelesen oder geschrieben werden, solange die Aktion nicht abgeschlossen ist
nicht-blockierendes Empfangen:
int MPI_Irecv(void *buf, int count, MPI_Datatype dtype, int source, int tag, MPI_Comm comm, MPI_Request *request);
Warten auf Beendigung des nicht-blockierenden Aufrufs:
int MPI_Wait( MPI_Request *request, MPI_Status *status );
Testen auf Beendigung des nicht-blockierenden Aufrufs:
int MPI_Test( MPI_Request *request, int *flag, MPI_Status *status );
Vorteil von nicht-blockierendem Aufruf: weniger Gefahr durch Deadlocks, Möglichkeit zum latency hiding
Beispiel für latency hiding mit IRECV:
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"
Nachteil: höhere Code-Komplexität → schwierigeres Debugging und schwierigere Wartung
Sendemodi (Senden abgeschlossen, wenn…)
Standard: Puffern der Nachricht durch MPI oder Synchronisieren der beiden beteiligten Prozesse
synchron: der empfangene Prozess muss begonnen haben, die Nachricht zu empfangen
Ready: ein passendes receive muss bereits vorliegen
buffered: MPI muss einen Puffer verwenden, der jedoch manuell gesteuert werden kann (MPIBUFFERATTACH
und MPIBUFFERDETACH
)
Kommunikation nicht-zusammenhängender Daten
üblicherweise werden Daten übertragen, die den gleichen Datentyp haben und in einem Array zusammenhängen
es können aber auch Daten übertragen werden, die nicht zusammenhängen
Beispiel: Submatrix versenden in C: (Anzahl Zeilen) * Nachricht mit (Anzahl Spalten) Elementen
for (i=0; i<n; ++i) {
MPI_Send(&a[k+i][l], m, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD);
}
Daten vor dem Versenden in einen zusammenhängenden Puffer kopieren und diesen versenden
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)
man sollte nicht in Versuchung geraten, Datentypen vor dem Übertragen eigenmächtig zu casten, da unterschiedliche MPI-Implementierungen die Werte falsch interpretieren könnten
stattdessen sollte 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 empfangen
Vorteil: man kann Nachrichten sukzessive erstellen und beliebige Datentypen verwenden
Nachteil: Overhead zum Verpacken der Daten
abgeleitete Datentypen
Kollektive Kommunikation
MPI stellt einige Funktionen zur Verfügung, um häufig benötigte Kommunikationsmuster zu implementieren
die Funktionen übertragen Nachrichten zu allen Prozessen einer Gruppe und daher muss jeder Prozess die Funktion aufrufen
int MPIBarrier ( MPIComm comm )
synchronisiert Prozesse ohne Daten zu übertragen; evtl. Overhead, daher sparsam verwenden
int 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
===== Links =====
* Parallelrechner und Parallelisierungskonzepte (Universität Karlsruhe)
* Parallele Systeme (Universität Münster)
* Parallele Programmierung (Uni Marburg)
===== 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)
* Metriken für Baum erstellen
se/parallelrechner.txt · Zuletzt geändert: 2014-04-05 11:42 (Externe Bearbeitung)