У вас есть утечка памяти в вашей программе; это:
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
раза за итерацию, предположительно за много итераций; но вы звоните только в ожидании одного из запросов. Вероятно, вам нужно выполнить MPI_Waitall()
для двух запросов.
Но помимо этого, ваша программа очень запутана. Никакая разумная программа MPI не должна иметь такой набор if (rank == ...)
операторов. И поскольку вы не выполняете никакой реальной работы между неблокирующей отправкой/получением и ожиданием, я не понимаю, почему вы просто не используете MPI_Sendrecv
или что-то в этом роде. Что ваша программа пытается выполнить?
ОБНОВЛЕНИЕ
Итак, похоже, вы делаете стандартное заполнение ореола. Несколько вещей:
Каждой задаче не нужны собственные массивы — A/AA для ранга 0, B/BB для ранга 1 и т. д. Память распределяется, а не разделяется; ни один ранг не может видеть другие массивы, поэтому не нужно беспокоиться о их перезаписи. (Если бы это было так, вам не нужно было бы отправлять сообщения). Кроме того, подумайте, насколько это усложняет работу с разным количеством процессов — вам придется добавлять новые массивы в код каждый раз, когда вы меняете количество используемых вами процессоров.
Вы можете читать/записывать непосредственно в массив V, а не использовать копии, хотя копии могут быть проще всего понять на начальном этапе.
Я написал здесь небольшую версию кода заполнения гало, используя ваши имена переменных (Tmyo
, Nmyo
, V
, индексы i
и y
и т. д.). Каждая задача имеет только свою часть более широкого массива V и обменивается краевыми данными только со своими соседями. Он использует символы, чтобы вы могли видеть, что происходит. Он заполняет свою часть массива V своим рангом #, а затем обменивается данными своего ребра со своими соседями.
Я НАСТОЯТЕЛЬНО рекомендую вам сесть за книгу MPI и поработать над ее примерами. Мне нравится использование MPI, но есть и многие другие. Есть также много хороших руководств по MPI. Думаю, не будет преувеличением сказать, что 95% книг и руководств по MPI (например, наши здесь - см. части 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();
}
Приведенную выше программу можно еще больше упростить, используя 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);
это признак того, что вы не используете правильные структуры данных. Здесь вы почти наверняка хотите иметь трехмерный массив переменных состояния, где (вероятно) третий индекс является видом или локальной переменной состояния или чем-то еще, что вы хотите назвать i2, i1f, i1s и т. д. Затем все эти строки можно заменить с циклом, и добавление новой локальной переменной состояния становится намного проще.
Точно так же, если все ваше состояние определяется как глобальные переменные, это значительно усложнит вашу жизнь, когда дело доходит до обновления и поддержки кода. Опять же, это, вероятно, частично связано с наличием вещей в миллионах независимых переменных состояния, а не со структурами или многомерными массивами, группирующими все соответствующие данные вместе.
person
Jonathan Dursi
schedule
06.06.2011