Sie haben ein Speicherleck in Ihrem Programm; dies:
MPI_Isend(A, Rows, MPI_DOUBLE, my_rank+1, 0, MPI_COMM_WORLD, &request[1]);
MPI_Irecv(AA, Rows, MPI_DOUBLE, my_rank+1, MPI_ANY_TAG, MPI_COMM_WORLD, &request[3]);
MPI_Wait(&request[3], &status[3])
leckt Ressourcen, die mit dem MPI_Isend
Anfrage. Sie nennen dies Rows*Columns
mal pro Iteration, über vermutlich viele Iterationen; aber Sie rufen Wait nur für eine der Anforderungen auf. Vermutlich müssen Sie eine MPI_Waitall()
für die beiden Anträge.
Aber darüber hinaus ist Ihr Programm sehr verwirrend. Kein vernünftiges MPI-Programm sollte eine solche Reihe von if (rank == ...)
Erklärungen. Und da Sie keine wirkliche Arbeit zwischen den nicht blockierenden Send/Recieves und den Waits machen, verstehe ich nicht, warum Sie nicht einfach MPI_Sendrecv
oder so ähnlich. Was soll mit Ihrem Programm erreicht werden?
UPDATE
Ok, es sieht so aus, als würdest du die übliche Halo-Füllung machen. Ein paar Dinge:
-
Jede Aufgabe macht no benötigen eigene Arrays - A/AA für Rang 0, B/BB für Rang 1 usw. Der Speicher ist verteilt, nicht geteilt; kein Rang kann die anderen Arrays sehen, also muss man sich keine Sorgen machen, dass sie überschrieben werden. (Wenn das der Fall wäre, bräuchte man keine Nachrichten zu senden). Überlegen Sie außerdem, wie viel schwieriger es ist, mit einer unterschiedlichen Anzahl von Prozessen zu arbeiten - Sie müssten jedes Mal, wenn Sie die Anzahl der verwendeten Prozessoren ändern, neue Arrays in den Code einfügen.
-
Sie können direkt in das V-Array lesen/schreiben, anstatt Kopien zu verwenden, obwohl die Kopien anfangs vielleicht am einfachsten zu verstehen sind.
Ich habe hier eine kleine Version eines Halo-Füllcodes geschrieben, der Ihre Variablennamen verwendet ( Tmyo
, Nmyo
, V
, Indizien i
y y
, etc). Jede Aufgabe hat nur ihren Teil des größeren V-Arrays und tauscht ihre Randdaten nur mit ihren Nachbarn aus. Es werden Zeichen verwendet, damit Sie sehen können, was vor sich geht. Sie füllt ihren Teil des V-Arrays mit ihrem Rang # aus und tauscht dann ihre Kantendaten mit ihren Nachbarn aus.
Ich würde SEHR GUT ermutigen Sie dazu, sich mit einem MPI-Buch hinzusetzen und die Beispiele durchzuarbeiten. Ich bin begeistert von MPI verwenden aber es gibt noch viele andere. Es gibt auch eine Menge guter MPI-Tutorials. Ich denke, es ist keine Übertreibung zu sagen, dass 95% der MPI-Bücher und -Tutorials (z.B. unseres aquí - siehe Teil 5 und 6) werden genau dieses Verfahren als eines ihrer ersten großen Arbeitsbeispiele durchgehen. Sie werden es Halo-Filling oder Guardcell-Filling oder Boundry-Exchange oder so nennen, aber es läuft alles auf die Weitergabe von Randdaten hinaus.
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
char **alloc_2d_char(const int rows, const int cols) {
char *data = (char *)malloc(rows*cols*sizeof(char));
char **array= (char **)malloc(rows*sizeof(char*));
for (int i=0; i<rows; i++)
array[i] = &(data[cols*i]);
return array;
}
void edgeDataFill(char **locV, const int locNmyo, const int locTmyo,
const int ncols, const int myrow, const int mycol,
const int size, const int rank) {
MPI_Datatype leftright, updown;
int left, right, up, down;
int lefttag = 1, righttag = 2;
int uptag = 3, downtag = 4;
MPI_Status status;
/* figure out our neighbours */
left = rank-1;
if (mycol == 0) left = MPI_PROC_NULL;
right = rank+1;
if (mycol == ncols-1) right = MPI_PROC_NULL;
up = rank - ncols;
if (myrow == 0) up = MPI_PROC_NULL;
down = rank + ncols;
if (down >= size) down = MPI_PROC_NULL;
/* create data type for sending/receiving data left/right */
MPI_Type_vector(locNmyo, 1, locTmyo+2, MPI_CHAR, &leftright);
MPI_Type_commit(&leftright);
/* create data type for sending/receiving data up/down */
MPI_Type_contiguous(locTmyo, MPI_CHAR, &updown);
MPI_Type_commit(&updown);
/* Send edge data to our right neighbour, receive from left.
We are sending the edge (locV[1][locTmyo]..locV[locNmyo][locTmyo]),
and receiving into edge (locV[0][1]..locV[locNmyo][locTmyo]) */
MPI_Sendrecv(&(locV[1][locTmyo]), 1, leftright, right, righttag,
&(locV[1][0]), 1, leftright, left, righttag,
MPI_COMM_WORLD, &status);
/* Send edge data to our left neighbour, receive from right.
We are sending the edge (locV[1][1]..locV[locNmyo][1]),
and receiving into edge (locV[1][locTmyo+1]..locV[locNmyo][locTmyo+1]) */
MPI_Sendrecv(&(locV[1][1]), 1, leftright, left, lefttag,
&(locV[1][locTmyo+1]), 1, leftright, right, lefttag,
MPI_COMM_WORLD, &status);
/* Send edge data to our up neighbour, receive from down.
We are sending the edge (locV[1][1]..locV[1][locTmyo]),
and receiving into edge (locV[locNmyo+1][1]..locV[locNmyo+1][locTmyo]) */
MPI_Sendrecv(&(locV[1][1]), 1, updown, up, uptag,
&(locV[locNmyo+1][1]), 1, updown, down, uptag,
MPI_COMM_WORLD, &status);
/* Send edge data to our down neighbour, receive from up.
We are sending the edge (locV[locNmyo][1]..locV[locNmyo][locTmyo]),
and receiving into edge (locV[0][1]..locV[0][locTmyo]) */
MPI_Sendrecv(&(locV[locNmyo][1]),1, updown, down, downtag,
&(locV[0][1]), 1, updown, up, downtag,
MPI_COMM_WORLD, &status);
/* Release the resources associated with the Type_create() calls. */
MPI_Type_free(&updown);
MPI_Type_free(&leftright);
}
void printArrays(char **locV, const int locNmyo, const int locTmyo,
const int size, const int rank) {
/* all these barriers are a terrible idea, but it's just
for controlling output to the screen as a demo. You'd
really do something smarter here... */
for (int task=0; task<size; task++) {
if (rank == task) {
printf("\nTask %d's local array:\n", rank);
for (int i=0; i<locNmyo+2; i++) {
putc('[', stdout);
for (int y=0; y<locTmyo+2; y++) {
putc(locV[i][y], stdout);
}
printf("]\n");
}
}
fflush(stdout);
MPI_Barrier(MPI_COMM_WORLD);
}
}
int main(int argc, char **argv) {
int ierr, size, rank;
char **locV;
const int Nmyo=12; /* horizontal */
const int Tmyo=12; /* vertical */
const int ncols=2; /* n procs in horizontal direction */
int nrows;
int myrow, mycol;
int locNmyo, locTmyo;
ierr = MPI_Init(&argc, &argv);
ierr|= MPI_Comm_size(MPI_COMM_WORLD, &size);
ierr|= MPI_Comm_rank(MPI_COMM_WORLD, &rank);
nrows = size/ncols;
if (nrows*ncols != size) {
fprintf(stderr,"Size %d does not divide number of columns %d!\n",
size, ncols);
MPI_Abort(MPI_COMM_WORLD,-1);
}
/* where are we? */
mycol = rank % ncols;
myrow = rank / ncols;
/* figure out how many Tmyo we have */
locTmyo = (Tmyo / ncols);
/* in case it doesn't divide evenly... */
if (mycol == ncols-1) locTmyo = Tmyo - (ncols-1)*locTmyo;
/* figure out how many Tmyo we have */
locNmyo = (Nmyo / nrows);
/* in case it doesn't divide evenly... */
if (myrow == nrows-1) locNmyo = Nmyo - (ncols-1)*locNmyo;
/* allocate our local array, with space for edge data */
locV = alloc_2d_char(locNmyo+2, locTmyo+2);
/* fill in our local data - first spaces everywhere */
for (int i=0; i<locNmyo+2; i++)
for (int y=0; y<locTmyo+2; y++)
locV[i][y] = ' ';
/* then the inner regions have our rank # */
for (int i=1; i<locNmyo+1; i++)
for (int y=1; y<locTmyo+1; y++)
locV[i][y] = '0' + rank;
/* The "before" picture: */
if (rank==0) printf("###BEFORE###\n");
printArrays(locV, locNmyo, locTmyo, size, rank);
/* Now do edge filling. Ignore corners for now;
the right way to do that depends on your algorithm */
edgeDataFill(locV, locNmyo, locTmyo, ncols, myrow, mycol, size, rank);
/* The "after" picture: */
if (rank==0) printf("###AFTER###\n");
printArrays(locV, locNmyo, locTmyo, size, rank);
MPI_Finalize();
}
Das obige Programm kann noch weiter vereinfacht werden durch MPI_Cart_create
um Ihren mehrdimensionalen Bereich zu erstellen und Ihre Nachbarn für Sie automatisch zu berechnen, aber ich wollte Ihnen die Logik zeigen, damit Sie sehen, was vor sich geht.
Außerdem sollten Sie sich Ratschläge von jemandem holen, der diese Arbeit schon lange macht:
Jedes Mal, wenn Sie Zeile für Zeile wiederholten Code haben: etwa 60 (!!) Zeilen davon:
Vmax =V[i][y]-Vold; updateMaxStateChange(Vmax / dt);
mmax=m[i][y]-mold; updateMaxStateChange(mmax / dt);
hmax=h[i][y]-hold; updateMaxStateChange(hmax / dt);
jmax=j[i][y]-jold; updateMaxStateChange(jmax / dt);
mLmax=mL[i][y]-mLold; updateMaxStateChange(mLmax / dt);
hLmax=hL[i][y]-hLold; updateMaxStateChange(hLmax / dt);
hLBmax=hLB[i][y]-hLBold; updateMaxStateChange(hLBmax / dt);
hLSmax=hLS[i][y]-hLSold; updateMaxStateChange(hLSmax / dt);
amax=a[i][y]-aold; updateMaxStateChange(amax / dt);
i1fmax=i1f[i][y]-i1fold; updateMaxStateChange(i1fmax / dt);
i1smax=i1s[i][y]-i1sold; updateMaxStateChange(i1smax / dt);
Xrmax=Xr[i][y]-Xrold; updateMaxStateChange(Xrmax / dt);
i2max=i2[i][y]-i2old; updateMaxStateChange(i2max / dt);
ist das ein Zeichen dafür, dass Sie nicht die richtigen Datenstrukturen verwenden. Hier wollen Sie mit ziemlicher Sicherheit ein 3d-Array von Zustandsvariablen haben, wobei (wahrscheinlich) der 3. Index die Spezies oder die lokale Zustandsvariable ist, oder wie immer Sie i2, i1f, i1s usw. nennen wollen. Dann können alle diese Zeilen durch eine Schleife ersetzt werden, und das Hinzufügen einer neuen lokalen Zustandsvariablen wird viel Einfacher.
Auch die Tatsache, dass im Wesentlichen der gesamte Zustand als globale Variablen definiert ist, wird Ihnen das Leben sehr viel schwerer machen, wenn es um die Aktualisierung und Pflege des Codes geht. Auch dies hängt wahrscheinlich teilweise damit zusammen, dass die Dinge in zig unabhängigen Zustandsvariablen gespeichert sind, anstatt Strukturen oder höherdimensionale Arrays zu haben, die alle relevanten Daten zusammenfassen.