2 Stimmen

MPI-Fehler: Out of Memory - Welche Lösungsmöglichkeiten gibt es?

Ich versuche zu klären Schwerwiegender Fehler in MPI_Irecv: Abbruch des Auftrags und erhielt gemischte (nützliche, aber unvollständige) Antworten auf diese Anfrage.

Le site Fehlermeldung ist die folgende:

aborting job:
> Fatal error in MPI_Irecv: Other MPI
> error, error stack: MPI_Irecv(143):
> MPI_Irecv(buf=0x8294a60, count=48,
> MPI_DOUBLE, src=2, tag=-1, 
> MPI_COMM_WORLD, request=0xffffd6ac)
> failed MPID_Irecv(64): Out of
> memory

Ich bitte jemanden um Hilfe bei der Beantwortung dieser Fragen (ich benötige eine Anleitung zur Fehlersuche und Behebung dieser Blockade)

  1. Wird der Speicher am Ende von "MPI Non Blocking Send and Receive" von selbst freigegeben, nachdem der Sende-/Empfangsvorgang abgeschlossen ist ODER muss die Freigabe erzwungen werden?

  2. Wird das Problem des fehlenden Arbeitsspeichers gelöst, wenn ich "mehrere Kerne" anstelle eines einzelnen Kerns verwende? Wir haben derzeit 4 Prozessoren zu 1 Kern und ich übermittle meinen Auftrag mit dem folgenden Befehl: mpirun -np 4 <file> . Ich habe versucht, mit mpirun n -4 <file> aber es liefen immer noch 4 Threads auf demselben Kern.

  3. Wie kann ich herausfinden, wie viel "Shared Memory" für mein Programm benötigt wird?

Le site MPI_ISend / MPI_IRecv befindet sich in einer rekursiven Schleife in meinem Code und daher ist nicht ganz klar, ob die Fehlerquelle dort liegt (Wenn ich die Befehle Send/Recv. nur ein- oder zweimal verwende, rechnet das System ohne "Out of Memory Issues" einwandfrei). Wenn ja, wie kann man solche Informationen überprüfen und entlasten?

#include <mpi.h>  

#define Rows 48 

double *A = new double[Rows];
double *AA = new double[Rows];
....
....

int main (int argc, char *argv[])
{
    MPI_Status status[8]; 
    MPI_Request request[8];
    MPI_Init (&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &p);   
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    while (time < final_time){
    ...
    ...

    for (i=0; i<Columns; i++) 
    {
        for (y=0; y<Rows; y++) 
        {
            if ((my_rank) == 0)
            {
                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]);  

                MPI_Isend(B, Rows, MPI_DOUBLE, my_rank+2, 0, MPI_COMM_WORLD, &request[5]);
                MPI_Irecv(BB, Rows, MPI_DOUBLE, my_rank+2, MPI_ANY_TAG, MPI_COMM_WORLD, &request[7]);
                MPI_Wait(&request[7], &status[7]);
            }

            if ((my_rank) == 1)
            {
                MPI_Irecv(CC, Rows, MPI_DOUBLE, my_rank-1, MPI_ANY_TAG, MPI_COMM_WORLD, &request[1]);
                MPI_Wait(&request[1], &status[1]); 
                MPI_Isend(Cmpi, Rows, MPI_DOUBLE, my_rank-1, 0, MPI_COMM_WORLD, &request[3]);

                MPI_Isend(D, Rows, MPI_DOUBLE, my_rank+2, 0, MPI_COMM_WORLD, &request[6]); 
                MPI_Irecv(DD, Rows, MPI_DOUBLE, my_rank+2, MPI_ANY_TAG, MPI_COMM_WORLD, &request[8]);
                MPI_Wait(&request[8], &status[8]);
            }

            if ((my_rank) == 2)
            {
                MPI_Isend(E, Rows, MPI_DOUBLE, my_rank+1, 0, MPI_COMM_WORLD, &request[2]);
                MPI_Irecv(EE, Rows, MPI_DOUBLE, my_rank+1, MPI_ANY_TAG, MPI_COMM_WORLD, &request[4]);
                MPI_Wait(&request[4], &status[4]);

                MPI_Irecv(FF, Rows, MPI_DOUBLE, my_rank-2, MPI_ANY_TAG, MPI_COMM_WORLD, &request[5]);
                MPI_Wait(&request[5], &status[5]);
                MPI_Isend(Fmpi, Rows, MPI_DOUBLE, my_rank-2, 0, MPI_COMM_WORLD, &request[7]);
            }

            if ((my_rank) == 3)
            {
                MPI_Irecv(GG, Rows, MPI_DOUBLE, my_rank-1, MPI_ANY_TAG, MPI_COMM_WORLD, &request[2]);
                MPI_Wait(&request[2], &status[2]);
                    MPI_Isend(G, Rows, MPI_DOUBLE, my_rank-1, 0, MPI_COMM_WORLD, &request[4]);

                MPI_Irecv(HH, Rows, MPI_DOUBLE, my_rank-2, MPI_ANY_TAG, MPI_COMM_WORLD, &request[6]);
                    MPI_Wait(&request[6], &status[6]); 
                    MPI_Isend(H, Rows, MPI_DOUBLE, my_rank-2, 0, MPI_COMM_WORLD, &request[8]);
            }
        }
    }
}

Danke!

6voto

Jonathan Dursi Punkte 48657

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:

  1. 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.

  2. 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.

1voto

John Punkte 2266

Ich bin mit der Bibliothek nicht vertraut, aber... 1) Sie sollten den Puffer nach dem Lesen nicht löschen. Sie haben den Puffer beim Programmstart (dynamisch) zugewiesen. Solange Sie ihn (einmal) beim Beenden löschen, sollte alles in Ordnung sein. Selbst wenn Sie ihn nicht löschen, sollte er beim Beenden des Programms aufgeräumt werden (aber das ist schlampig).

2) Mehrere Kerne sollten keine Auswirkungen auf ein Speicherproblem haben.

3) Nicht sicher. MPI sollte eine Dokumentation haben, die Ihnen hilft.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X