Ash*_*han 2 c++ parallel-processing multicore mpi shared-memory
我正在尝试解决MPI_Irecv中的致命错误:中止作业并收到对该查询的混合(有用但不完整)响应.
该错误消息是以下内容:
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
Run Code Online (Sandbox Code Playgroud)
我正在寻求某人的帮助来回答这些问题(我需要指导以帮助调试和解决此死锁)
在"MPI非阻塞发送和接收"结束时,发送/接收完成后是自己释放的内存还是必须强制释放它?
如果我使用"多核心"而不是单核心,会解决"内存不足"的问题吗?我们目前有4个处理器到1个核心,我使用以下命令提交我的工作:mpirun -np 4.我尝试使用mpirun n -4但它仍然在同一个核心上运行了4个线程.
我如何计算出我的程序需要多少"共享内存"?
MPI_ISend/MPI_IRecv在我的代码中的递归循环内部,因此如果错误源位于那里则不太清楚(如果我只使用一次或两次发送/接收命令,系统计算就好了没有"内存不足问题"如果是这样,如何检查和减轻此类信息?
#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]);
}
Run Code Online (Sandbox Code Playgroud)
谢谢!
你的程序中有内存泄漏; 这个:
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])
Run Code Online (Sandbox Code Playgroud)
泄漏与MPI_Isend请求相关的资源.你可以在Rows*Columns每次迭代中调用这个次数,大概是多次迭代; 但你只是在等待其中一个请求.你可能需要MPI_Waitall()为这两个请求做一个.
但除此之外,你的程序非常混乱.没有明智的MPI计划应该有这样一系列的if (rank == ...)陈述.既然你没有在非阻塞发送/接收和等待之间做任何实际的工作,我不明白你为什么不只是使用MPI_Sendrecv或什么.你的计划试图完成什么?
UPDATE
好的,所以看起来你正在做标准的光环填充物.一些东西:
每个任务都不需要它自己的数组 - 排名为0的A/AA,排名1的B/BB,等等.内存是分布式的,不是共享的; 没有排名可以看到其他阵列,所以没有必要担心覆盖它们.(如果有,则不需要发送消息).此外,想想在不同数量的进程上运行会变得多么困难 - 每次更改所使用的处理器数量时,都必须在代码中添加新数组.
您可以直接读取/写入V阵列而不是使用副本,尽管最初可能最容易理解副本.
我已经写在这里使用的变量名(光环填充代码一点的版本Tmyo,Nmyo,V,indicies i和y等).每个任务只有更宽V阵列的一部分,并且仅与其邻居交换其边缘数据.它使用字符,因此您可以看到正在发生的事情.它用其排名#填充其V阵列的一部分,然后与其邻居交换其边缘数据.
我强烈建议您坐下来阅读MPI书并完成其示例.我喜欢使用MPI,但还有很多其他的.还有很多很好的MPI教程.我认为这是毫不夸张的说,MPI书籍和教程95%(例如,我们在这里 -见部分5和6)将经历正是这一程序作为其第一大合作的例子之一.他们会称之为光环填充或保护单元填充或边界交换或其他东西,但这一切都归结为通过边缘数据.
#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();
}
Run Code Online (Sandbox Code Playgroud)
上面的程序可以进一步简化,MPI_Cart_create用于创建你的多维域并自动为你计算你的邻居,但我想向你展示逻辑,以便你看到发生了什么.
此外,如果您能从长期以来完成此操作的人那里获得一些建议:
任何时候你都有一行一行的重复代码:像60(!!)行:
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);
Run Code Online (Sandbox Code Playgroud)
这表明您没有使用正确的数据结构.在这里,你几乎肯定想要一个3d数组的状态变量,(可能)第三个索引是物种或本地状态变量或任何你想要调用i2,i1f,i1s等等.然后所有这些行都可以被替换使用循环,添加新的本地状态变量变得更加简单.
同样,基本上所有的状态都被定义为全局变量,这将使你的生活在更新和维护代码时变得更加困难.同样,这可能部分与以数十亿个独立状态变量的东西相关,而不是将所有相关数据组合在一起的结构或更高维数组.
| 归档时间: |
|
| 查看次数: |
2885 次 |
| 最近记录: |