Grenzen und Umfang eines MPI-Datentyps
Wenn wir nicht den glücklichen Umstand haben, dass die Matrix in row major mit A.incCol == 1 vorliegt, wenn diese in row major zu übertragen ist, dann ist es prinzipiell möglich, den Datentyp einer Matrix in zwei Schritten zu erzeugen:
-
Vektor-Datentyp für eine Zeile bestimmen
-
Matrix als Vektor von Vektoren ansehen
Dabei ist jedoch zunächst unklar, wie MPI komplexe Datentypen zu einem Vektor aneinanderreiht. Bei unmittelbar im Speicher hintereinander liegenden Datentypen ist dies klar. Im Falle einer col major-Matrix mit A.incRow == 1 liegen die Zeilen nicht konsekutiv hintereinander im Speicher, sondern überlappen sich mit einem Versatz.
Um zu verstehen, wie MPI diese Dinge betrachtet, sollten wir zur Definition eines Datentyps als Tupelsequenz zurückkehren: \(T = \left\{ (bt_1, o_1), (bt_2, o_2), \dots, (bt_n, o_n) \right\}\),
-
Für jeden Datentyp \(T\) sein der Umfang \(\mbox{extent}(T)\) definiert. Bei den elementaren Datentypen entspricht dies dem Wert, den sizeof liefern würde. Beispielsweise ist \(\mbox{extent}(\mbox{MPI_DOUBLE}) = 8\).
-
Der lower bound \(\mbox{lb}\) ist definiert als \(\mbox{lb}(T) = \min\limits_{1 \le i \le n} \left\{ o_i \right\}\).
-
Analog gibt es den upper bound \(\mbox{ub}\), der jedoch den Umfang mit berücksichtigt: \(\mbox{ub}(T) = \max\limits_{1 \le i \le n} \left\{ o_i + \mbox{extent}(bt_i) \right\} + \epsilon\). Hierbei dient \(\epsilon\) der Aufrundung auf die nächste Alignment-Kante.
-
Dann lässt sich \(\mbox{extent}\) allgemein definieren: \(\mbox{extent}(T) = \mbox{ub}(T) - \mbox{lb}(T)\).
Angenommen, wir hätten eine \(2 \times 3\)-Matrix in col major. Der Datentyp \(R\) für die erste Zeile würde dann so aussehen: \(R = \left\{ (DOUBLE, 0), (DOUBLE, 16), (DOUBLE, 32) \right\}\). Dann ist \(\mbox{lb}(R) = 0\), \(\mbox{ub}(R) = 40\) und \(\mbox{extent}(R) = \mbox{ub}(R) - \mbox{lb}(R) = 40\). Würden wir jetzt zwei solche Zeilen zu einem Vektor kombinieren, wäre das erste absolute Offset des zweiten Zeilenvektors 40, obwohl dieser bei 8 beginnt.
Die MPI-Bibliothek erlaubt es, von der voreingestellten Berechnung mit Hilfe der Funktion MPI_Type_create_resized abzuweichen:
int MPI_Type_create_resized(MPI_Datatype oldtype, MPI_Aint lb,
MPI_Aint extent, MPI_Datatype* newtype);
Hierbei spezifiziert lb die neue untere Grenze \(\mbox{lb}(T)\) und extent den Umfang \(\mbox{extent}(T)\), der in Bytes zu spezifizieren ist. Daraus ergibt sich dann implizit auch \(\mbox{ub}(T)\). Es ist dabei zulässig, den Umfang zu wählen, dass zwei “konsekutiv” folgende Objekte sich überlappen. Im obigen Beispiel der col major-Matrix wäre es entsprechend sinnvoll, dem Umfang auf 8 Bytes zu setzen, da &A(0, 0) und &A(1, 0) nur 8 Bytes voneinander entfernt sind.
Im Trivialfall, dass alle Elemente eines Vektors unmittelbar hintereinander liegen, kann auch MPI_Type_contiguous verwendet werden:
int MPI_Type_contiguous(int count, MPI_Datatype oldtype, MPI_Datatype* newtype);
Aufgabe
-
Entwickeln Sie eine Funktion get_row_type, die den MPI-Datentyp für die Zeile einer Matrix liefert, wobei mit Hilfe der Funktion MPI_Type_create_resized der Umfang so konfiguriert wird, dass der gleiche Datentyp für aufeinanderfolgende Zeilen einer beliebigen Matrix verwendet werden kann. lb kann dabei unverändert auf 0 bleiben.
-
Entwickeln Sie darauf aufbauend get_type für Matrizen, wobei im Falle einer Matrix mit A.incCol != 1 die Funktion get_row_type verwendet wird, um den MPI-Datentyp als Vektor von Zeilen zu konstruieren.
-
Vergleichen Sie den Umfang des von Ihnen konstruierten Matrix-Typs mit der Größe der Matrix:
GeMatrix<double> A(m, n); MPI_Datatype datatype_A = get_type(A); MPI_Aint true_extent; MPI_Aint true_lb; MPI_Type_get_true_extent(datatype_A, &true_lb, &true_extent); MPI_Aint extent; MPI_Aint lb; MPI_Type_get_extent(datatype_A, &lb, &extent); auto size = sizeof(double) * A.numRows * A.numCols; assert(extent == size && true_extent == size);
MPI_Type_get_extent liefert den möglicherweise manipulierten Umfang, MPI_Type_get_true_extent liefert diesen ohne die Manipulationen durch MPI_Type_create_resized.
-
Schreiben Sie darauf aufbauend ein kleines Testprogramm mit zwei Prozessen für Matrizen analog zum vorherigen Testprogramm mit Vektoren.
Überprüfen Sie sicherheitshalber, dass der Umfang der gesamten Matrix korrekt ist:
Testen Sie Ihr Programm dabei mit unterschiedlichen Konfigurationen. Beispielsweise die Übertragung einer row major-Matrix in eine col major-Matrix oder umgekehrt.