C++ MPI错误:内存不足-有哪些解决方案选项

C++ MPI错误:内存不足-有哪些解决方案选项,c++,multicore,mpi,parallel-processing,shared-memory,C++,Multicore,Mpi,Parallel Processing,Shared Memory,我正试图解决这个问题,但收到了对该问题的混合(有用,但不完整)回答 错误消息如下所示: 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=0xffffd6

我正试图解决这个问题,但收到了对该问题的混合(有用,但不完整)回答

错误消息如下所示:

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
我正在寻求他人的帮助以回答这些问题(我需要指导以帮助调试和解决此死锁)

  • 在“MPI非阻塞发送和接收”的末尾,内存是在发送/接收完成后自行释放还是必须强制释放

  • 如果我使用“多核”而不是单核,“内存不足”的问题会得到解决吗?。我们目前有4个处理器与1个内核,我使用以下命令提交作业:
    mpirun-np4
    。我尝试使用
    mpirun n-4
    ,但它仍然在同一个内核上运行4个线程

  • 如何计算我的程序需要多少“共享内存”

  • MPI\u ISend
    /
    MPI\u IRecv
    在我的代码中位于一个递归循环中,因此不太清楚错误的来源是否在那里(如果我只使用Send/Recv.命令一两次,系统计算很好,没有“内存不足问题”)。如果是,如何检查和释放这些信息

    #包括
    #定义第48行
    double*A=新的double[行];
    double*AA=新的double[行];
    ....
    ....
    int main(int argc,char*argv[])
    {
    MPI_状态[8];
    MPI_请求[8];
    MPI_Init(&argc,&argv);
    MPI通信大小(MPI通信世界和p);
    MPI通信等级(MPI通信世界和我的通信等级);
    while(时间<最终时间){
    ...
    ...
    
    对于(i=0;i我不熟悉这个库,但是。。。 1) 您不应该在读取后删除缓冲区。您在程序启动时(动态)分配了缓冲区。只要在终止时删除它(一次),您就可以了。实际上,即使您不删除它,在程序退出时它也应该得到清理(但这太草率了)

    2) 多核应该对内存问题没有影响


    3) 不确定。MPI应该有一些文档可以帮助您。

    您的程序内存泄漏;这:

    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
    请求相关的资源。您在每次迭代中调用此
    Rows*Columns
    次,可能需要多次迭代;但您只调用Wait for a request。您可能需要对这两个请求执行
    MPI_Waitall()

    但除此之外,您的程序非常混乱。任何明智的MPI程序都不应该有这样一系列的
    if(rank==…)
    语句。既然您在非阻塞发送/接收和等待之间没有做任何实际工作,我不明白为什么您不只是使用
    MPI\u Sendrecv
    或其他什么。您的程序试图实现什么

    更新

    好的,看起来你在做标准的光环填充。有几件事:

  • 每个任务不需要自己的数组—A/AA表示秩0,B/BB表示秩1,等等。内存是分布式的,而不是共享的;没有任何秩可以看到其他数组,因此无需担心覆盖它们。(如果有,您就不需要发送消息)。此外,想想这会使在不同数量的进程上运行变得多么困难—每次更改使用的处理器数量时,都必须向代码中添加新的数组

  • 您可以直接读/写到V数组中,而不是使用副本,尽管副本最初可能最容易理解

  • 我在这里用变量名写了一个小版本的光环填充代码(
    Tmyo
    Nmyo
    V
    ,指示符号
    I
    y
    ,等等)。每个任务只有较宽的V数组中的一部分,并且只与相邻任务交换其边缘数据。它使用字符以便您可以看到发生了什么。它用秩#填充V数组中的部分,然后与相邻任务交换其边缘数据

    我强烈地鼓励你坐下来看一本MPI书籍,并仔细阅读它的例子。我喜欢,但还有很多其他的。还有很多很好的MPI教程。我认为毫不夸张地说,95%的MPI书籍和教程(例如,我们的-见第5部分和第6部分)作为他们的第一个大型工作示例之一,将严格执行此过程。他们将其称为halo填充或guardcell填充或边界交换或其他内容,但这一切都归结为传递边缘数据

    #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();
    }
    
    这表明您没有使用正确的数据结构第三个索引是物种或局部状态变量,或者任何你想称之为i2、i1f、i1s等的东西。然后所有这些行都可以用一个循环来代替,添加一个新的局部状态变量变得简单多了


    类似地,当更新和维护代码时,将所有状态定义为全局变量将使您的生活变得更加艰难。同样,这可能与拥有大量独立状态变量,而不是将所有相关的结构或更高维数组分组有关ant数据一起使用。

    使用多核而不是单核并不能解决内存不足的问题,它仍然是计算机中的同一个ram,不管是一个内核使用它还是4个内核使用它!@Gajet:这是在集群上运行的…所以我想,每个内核都有自己的共享内存。因此,我的假设是,内存问题可能会得到解决!?然而,我不确定,因此我的想法。1.阅读代码不是问题所在,调试才是问题所在。2.如果错误消息比“内存不足”或“其他MPI错误”更具体,那么这个假设可能成立;在这种情况下,信息越多越好。3.使用dropbox链接编辑这篇文章,例如,为那些
    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);