Ошибка MPI: нехватка памяти — какие варианты решения

Я пытаюсь устранить Fatal Error in MPI_Irecv: Aborting Job и получил смешанные (полезные, но неполные) ответы на этот запрос.

Сообщение об ошибке выглядит следующим образом:

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

Мне нужна помощь, чтобы ответить на эти вопросы (мне требуется руководство, чтобы помочь отладить и устранить эту тупиковую ситуацию)

  1. В конце неблокирующей отправки и получения MPI память освобождается сама по себе после завершения отправки/получения ИЛИ ее необходимо принудительно освобождать?

  2. Будет ли решена проблема нехватки памяти, если я использую несколько ядер вместо одного? В настоящее время у нас есть 4 процессора на 1 ядро, и я отправляю свою работу с помощью следующей команды: mpirun -np 4 <file>. Я пытался использовать mpirun n -4 <file>, но он по-прежнему запускал 4 потока на одном ядре.

  3. Как узнать, сколько общей памяти требуется для моей программы?

MPI_ISend/MPI_IRecv находится внутри рекурсивного цикла в моем коде и, следовательно, не очень ясно, находится ли там источник ошибки (если я использую команды Send/Recv. только один или два раза, система работает нормально без проблем с нехваткой памяти). Если да, то как можно проверить и удалить такую ​​информацию?

#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]);
            }
        }
    }
}

Спасибо!


person Ashmohan    schedule 06.06.2011    source источник
comment
использование многоядерного вместо одноядерного не решает проблему нехватки памяти, это все тот же оперативная память на вашем компьютере, не имеет значения, использует ли ее одно ядро ​​​​или 4 ядра!   -  person Ali1S232    schedule 07.06.2011
comment
@Gajet: это выполняется в кластере ... поэтому я полагаю, что у каждого ядра есть собственная общая память. Следовательно, мое предположение, что проблема с памятью, возможно, может быть решена!? Однако, я не был уверен и следовательно идея.   -  person Ashmohan    schedule 07.06.2011
comment
1. Чтение кода не так важно; отладка это. 2. Если бы сообщения об ошибках были чем-то более конкретным, чем «Недостаточно памяти» или «Другая ошибка MPI», то это предположение, вероятно, было бы верным; в этом случае, чем больше информации, тем лучше. 3. Редактирование этого поста, например. ссылка на дропбокс для тех, кто интересуется отладкой, будет полезна; другой новый поток определенно не гарантируется.   -  person ildjarn    schedule 07.06.2011


Ответы (2)


У вас есть утечка памяти в вашей программе; это:

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 или что-то в этом роде. Что ваша программа пытается выполнить?

ОБНОВЛЕНИЕ

Итак, похоже, вы делаете стандартное заполнение ореола. Несколько вещей:

  1. Каждой задаче не нужны собственные массивы — A/AA для ранга 0, B/BB для ранга 1 и т. д. Память распределяется, а не разделяется; ни один ранг не может видеть другие массивы, поэтому не нужно беспокоиться о их перезаписи. (Если бы это было так, вам не нужно было бы отправлять сообщения). Кроме того, подумайте, насколько это усложняет работу с разным количеством процессов — вам придется добавлять новые массивы в код каждый раз, когда вы меняете количество используемых вами процессоров.

  2. Вы можете читать/записывать непосредственно в массив 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
comment
Спасибо Джонатан! Проблема аналогична задаче ближайшего соседа. У меня есть четыре узла, каждый из которых передает информацию о массиве краев своему ближайшему соседу. Так, например, узел 0 отправляет и получает информацию о границах от узлов 1 и 3 и так далее. 2. Я не был уверен, смогу ли я использовать MPI_Sendrecv для вышеуказанной проблемы. 3. Не совсем понятно ваш комментарий. Никакая разумная программа MPI не должна иметь ряд, если Rank=0.. Как еще вы предлагаете мне решить эту проблему? Вы бы порекомендовали мне опубликовать мой код целиком? Я этого не сделал, так как не ожидал, что кто-то проанализирует все строки. - person Ashmohan; 07.06.2011
comment
@Jonathan: Кроме того, просто хотел убедиться, что я правильно интерпретирую ваш комментарий ... Вы предполагаете, что MPI_Sendrecv будет просто последовательностью комментариев, когда я хочу передать данные, а не быть инкапсулированным If (rank == 0 ) тип комментариев? - person Ashmohan; 07.06.2011
comment
Жесткое кодирование в таких рангах означает, что если вы когда-нибудь захотите использовать другое количество процессоров, вам придется полностью переписать программу. Похоже, вы связываетесь с ближайшими соседями, чтобы обменяться ореолами. Как-нибудь завтра я попытаюсь опубликовать более простую версию этого. - person Jonathan Dursi; 07.06.2011
comment
Спасибо, Джонатан. Я тоже это понял, но мне нужна была функциональная реализация MPI, прежде чем я сделал ее более общей. Ваш вклад в это будет очень полезен. Спасибо! - person Ashmohan; 07.06.2011
comment
Обновлено с базовой структурой того, что вы хотите сделать. Обратите внимание, что приведенная выше программа будет работать с любым количеством процессоров ncols, и было бы достаточно просто (избегая жесткого кодирования ncols) работать с любым количеством процессоров. Также обратите внимание, что как только у нас есть ncols и как только мы определили константы для Nmyo и Tmyo и т. д., мы никогда больше не будем использовать эти жестко закодированные числа. Это важно для удобства сопровождения. - person Jonathan Dursi; 07.06.2011
comment
@Jonathan: Требуются некоторые разъяснения по коду, который вы предложили, в частности, для выяснения соседей. ВОТ КОД '/* вычисляем наших соседей */left = rank-1;if (mycol == 0) left = MPI_PROC_NULL;right = rank+1;if (mycol == ncols- 1) справа = MPI_PROC_NULL; вверх = ранг - ncols; если (myrow == 0) вверх = MPI_PROC_NULL; вниз = ранг + ncols; if (вниз ›= размер) вниз = MPI_PROC_NULL;'. Если я предполагаю ncols=4, то для ранга 0 (скажем) слева = -1, справа = +1, вверх = -4 и вниз = +4. Графически я ранее представлял 4 узла в сетке 2x2. Я не был ясен. Пожалуйста, порекомендуйте. Спасибо! - person Ashmohan; 18.06.2011
comment
В main() для ncols установлено значение 2; поэтому, если бы у вас был размер = 4, у вас была бы сетка 2 на 2 (nrows = size/ncols;), а влево/вправо/вверх/вниз = -1/+1/-2/+2. Но, конечно, вы можете иметь любую декомпозицию, которую хотите. Если у вас есть size задач, вы можете самостоятельно разложить их по строкам/столбцам или вызвать MPI_Dims_create(size, 2, dims) для создания как можно более квадратной декомпозиции, а затем rows=dims[0]; cols=dims[1];. - person Jonathan Dursi; 18.06.2011
comment
Спасибо! (также для обмена ссылками). Ваш сайт был особенно полезен. хотя, ничего похожего на практическое занятие! Я пытался усвоить и переварить ваши предложения. Отсутствие очень сильного опыта в C/C++ бесполезно, поэтому прогресс был медленным. Я хотел, чтобы вы ответили на два вопроса: 1. Основываясь на моем исходном коде (опубликованном выше), что я делал неправильно? Была ли неправильной структура того, как я генерировал динамические массивы? Мне любопытно, просто с точки зрения обучения, почему в моем коде возникла проблема с утечкой памяти? и что было бы самым простым решением для этого. - person Ashmohan; 19.06.2011
comment
Судя по коду, я бы сказал, что в то время вы не были полностью довольны моделью SPMD и распределенного программирования, которая лежит в основе большинства программ MPI. Проблемы с неблокирующими отправками/получениями + ожидание были наименьшими из проблем, и я думаю, что они просто возникли из-за попытки использовать эти более продвинутые методы слишком быстро, когда более простые подходы (sendrecv) работали бы так же хорошо. Более фундаментальным является жесткое кодирование количества процессов и использование каждым процессором своих собственных массивов (A / AA, B / BB и т. Д.), Которые не нужны (и очень сложны в обслуживании). - person Jonathan Dursi; 19.06.2011
comment
Утечка памяти возникает из-за того, что каждый isend/irecv создает запрос, который требует некоторых ресурсов (например, памяти). Вы освобождаете эту память, удаляя запрос, например, выполняя Wait() или Waitall() для каждого запроса. Создавая все больше и больше запросов, но вызывая Wait() только для некоторых из них, вы освобождали только некоторую часть памяти, и, таким образом, у вас была утечка памяти. - person Jonathan Dursi; 19.06.2011
comment
Привет Джонатан! Еще раз спасибо и большое спасибо за ваше терпение! К вашему сведению, я действительно пытался воспроизвести лист размером 96 x 96. Поэтому я заставил каждый узел вычислять лист 48 x 48. Ваши недавние комментарии вызывают два вопроса: 1. Если я просто использую A/AA для всех узлов и использую Waitall, решит ли это теоретически проблему утечки памяти? Кроме того, помимо использования MPI_Sendrecv, есть ли другие команды, которые могут помочь мне заполнить защитную ячейку? - person Ashmohan; 20.06.2011
comment
Ashoman: Проблема с утечкой памяти устранена за счет предотвращения утечки ресурсов путем отправки неблокирующих запросов без их ожидания. Вы можете исправить это, (а) ожидая каждого запроса, который вы отправляете в какой-то момент, что вам в любом случае необходимо для корректности, и/или (б) не используя неблокирующие коммуникации и используя sendrecv, как в пример выше. Приведенный выше код представляет собой полную и правильную процедуру заполнения защитных ячеек, которая вообще не использует временные массивы (например, A/AA). - person Jonathan Dursi; 20.06.2011
comment
Привет Джонатан: Как будет использовать этот синтаксис: MPI_Sendrecv(&A, Tmyo, MPI_DOUBLE, my_rank+1, 0, &CC, Tmyo, MPI_DOUBLE, my_rank-1, 0, MPI_COMM_WORLD, &status); отличаться от того, что вы рекомендовали, то есть вы создаете тип данных для отправки и получения данных. То, как я определяю свой send_recv, также должно быть непрерывным, верно? - person Ashmohan; 21.06.2011
comment
Да, если вы (без необходимости) копируете в специально созданные буферы и из них, вам не потребуются специальные типы данных, и ваши данные будут непрерывными. - person Jonathan Dursi; 21.06.2011
comment
В порядке. Я попытался настроить MPI_Sendrecv, и когда я проверял статус отправки с помощью top, я заметил, что задание отправляется на указанные узлы, но немедленно завершается без каких-либо ошибок (чего не должно быть). Возникают два вопроса: 1. Какая часть настройки Sendrecv может привести к такой ошибке? 2. Не могли бы вы порекомендовать отладчик, который я мог бы использовать для пошагового выполнения кода (в MPI), строка за строкой. Также на основе ваших лекций я установил IPM на кластер. Единственный способ использовать/выявить узкие места — это вставить комментарии MPI в код, верно? - person Ashmohan; 22.06.2011
comment
Кроме того, не могли бы вы объяснить некоторые строки кода, заполнить наши локальные данные - первые пробелы везде 'for (int i=0; i‹locNmyo+2; i++) for (int y =0;y‹locTmyo+2;y++) locV[i][y] = ' ';' тогда внутренние области имеют наш ранг for (int i=1; i‹locNmyo+1; i++) for (int y=1; y‹locTmyo+1; y++) locV[i][y ] = '0' + ранг;' Большое Вам спасибо. - person Ashmohan; 22.06.2011
comment
Я не могу отлаживать вашу программу, которая немедленно завершается без ошибок, основываясь только на заявлении, которое она делает. Вам нужно будет начать новый вопрос и предоставить исходный код. Отладчики: для небольшого количества процессоров можно запускать один gdb на процесс; В mpich2 есть опция для этого, а для openmpi — open-mpi. de/faq/?category=debugging#serial-debuggers . Коммерческие решения, такие как ddt или totalview, великолепны, но очень дороги. IPM — это хорошее начало для профилирования вашего кода MPI, но первый шаг — заставить все работать правильно; производительность - вопрос второго порядка. - person Jonathan Dursi; 22.06.2011
comment
Что касается инициализации, она делает именно то, что кажется: locV представляет собой массив символов для простоты вывода на печать, чтобы вы могли видеть, что происходит. Надеюсь, вы запустили программу, чтобы увидеть, что происходит. Вы заполняете все это пробелами (включая ребра, которые вы получите от своих соседей) для инициализации вещей, а затем вы заполняете внутреннюю область (не ребра, которые вы получаете от своих соседей) данными, которые идентифицируют его ранг. был заполнен -- просто символьным представлением номера ранга. - person Jonathan Dursi; 22.06.2011
comment
Спасибо Джонатан! Я запустил ваш исходный код и отправил его с помощью mpirun -np XX filename.cpp. Я заметил следующее: когда XX = 4 процессора, ваш исходный код генерирует матрицу 6x6 для каждого ранга, а затем ребро After заполняется рангом соседа. Что хорошо. Когда я увеличиваю XX до 6, я замечаю что-то странное (то есть то, что я не могу понять, просто глядя на результаты)... каждый ранг теперь имеет матрицу 6x4 (ранги 0, 1, 2, 3) и ранги 4 и 5. есть матрица 6х8? - person Ashmohan; 22.06.2011
comment
Может быть, это может быть просто мое неправильное понимание того, что вы пытались сделать. Я предположил, что существует один массив размером Nmyo x Tmyo, который вы распределяете между процессорами. (Обратите внимание, что то, как я делаю это разбиение, является самым простым способом, подходящим для небольших примеров, но не обязательно способом, который ближе всего к равномерному разделению; есть лучшие способы сделать это, чтобы все получилось немного более равномерно. Но если ваша сетка достаточно велика, эти вопросы дискретности становятся все менее и менее важными). - person Jonathan Dursi; 22.06.2011
comment
С другой стороны, если вы хотите, чтобы ваша локальная сетка имела одинаковый размер для каждого процессора, а глобальная сетка должна меняться в зависимости от количества процессоров, вы должны сделать это по-другому. Или, если бы были другие ограничения (вы хотите, чтобы каждый процессор имел домен одинакового размера, но сам размер может различаться), вам пришлось бы реализовать это. Если вы делаете обычную 2d-pde-on-a-grid вещь, то способ, которым я это выше, является обычным способом сделать это (плюс-минус небольшие уточнения). - person Jonathan Dursi; 22.06.2011
comment
Спасибо за ответ. Nmyo и Tmyo — это переменные, определяющие количество клеток в каждом измерении. Скажем, в случае, когда -np XX=4, я хотел, чтобы каждый процессор запускал Nmyo X Tmyo 2D-лист. В случае с 4 процессорами я хотел, чтобы грани взаимодействовали. Вот и все. Я столкнулся с проблемами при выполнении MPI_Isend. Я выбрал это, так как чувствовал, что могу сэкономить процессорное время. На данный момент мое внимание сосредоточено на запуске MPI для обмена данными в течение нескольких временных шагов. Мне удалось выполнить передачу статического массива краев (как в приведенном выше примере, хотя жестко запрограммировано только для 4 процессоров), а затем и для больших. - person Ashmohan; 22.06.2011
comment
Первоначально я предполагал, что ваш код сохранит локальную сетку без изменений, но сможет масштабироваться в зависимости от изменений в глобальной сетке. Как вы предложили, я создам альтернативный поток, чтобы показать вам, что я пробовал с MPI_Sendrecv. Если бы вы могли просто помочь мне определить, что я делаю неправильно. Это поможет мне хотя бы перейти к профилированию моего кода и работе над его улучшением. Я искал местные ресурсы, чтобы помочь (включая использование MPI), но пока это не сработало для меня. Спасибо - Ашвин - person Ashmohan; 22.06.2011
comment
Я создал новую тему для проблемы MPI_Sendrecv. Вот ссылка на вопрос: ‹stackoverflow.com/questions/6445234 /issue-with-mpi-sendrecv - person Ashmohan; 22.06.2011

Я не знаком с библиотекой, но... 1) Не следует удалять буфер после чтения. Вы выделили буфер (динамически) при запуске программы. Пока вы удаляете его (один раз) при завершении, все будет в порядке. На самом деле, даже если вы его не удалите, он должен очиститься при выходе из программы (но это неаккуратно).

2) Несколько ядер не должны влиять на проблему с памятью.

3) Не уверен. У MPI должна быть документация, которая поможет вам.

person John    schedule 06.06.2011
comment
Спасибо, Джон! Не могли бы вы уточнить, что вы подразумеваете под удалением буфера при завершении. - person Ashmohan; 07.06.2011
comment
Джон, я попробовал то, что вы рекомендовали, и безуспешно? Есть ли что-то еще, что я могу попробовать? - person Ashmohan; 26.06.2011