У меня проблема с MPI_Isend
а также MPI_Irecv
, Я работаю над матрицей смежности графа, который распределяется по строкам. Можно предположить, что каждый процессор содержит одну строку. Для каждой пары показателей (i,j)
Мне нужно отправить и получить 2 целых числа. По сути, мне нужно получить некоторую другую информацию из других строк для выполнения вычислений. Я новичок в MPI, и вот он идет в бесконечный цикл, я не уверен, даже это правильный способ использования MPI_Isend
или же MPI_Irecv
в цикле, а также место ожидания.
Например, предположим, что у нас есть граф с 6 вершинами, и поэтому матрица смежности (adjMatrix
) будет матрица 6 * 6, у нас также есть матрица 6 * 2 для некоторой другой информации, и, наконец, мы распределяем данные по 6 процессорам. Следовательно:
|0 20 16 0 6 0 | |0 1|
|20 0 0 19 0 6 | |1 1|
addMatrix=|16 0 0 0 12 0 | M=|2 1|
|0 19 0 0 0 12| |3 1|
|6 0 12 0 0 9 | |0 0|
|0 6 0 12 9 0 | |1 0|
Распределим матрицы следующим образом:
P0: |0 20 16 0 6 0 | |0 1|
P1: |20 0 0 19 0 6 | |1 1|
P2: |16 0 0 0 12 0 | |2 1|
P3: |0 19 0 0 0 12| |3 1|
P4: |6 0 12 0 0 9 | |0 0|
P5: |0 6 0 12 9 0 | |1 0|
Теперь каждый процессор должен обновить свою часть adjMatrix
, Для этого им нужна информация из некоторой части матрицы M
, что есть в других процессорах. Например, чтобы P0
индекс обновлений (0,1)
который 20
, он должен иметь доступ к строке 1
матрицы M
который {1,1}
, Следовательно:
P1
должен отправитьMLocal[0][0]=1
а такжеMLocal[0][1]=1
вP0
в которомP0
получает их какM_j0
а такжеM_j1
соответственно.А также
P0
должен отправитьMLocal[0][0]=0
а такжеMLocal[0][1]=1
вP1
в которомP1
получает их какM_j0
а такжеM_j1
соответственно.
for(int i=0;i<rows;i++){
for (int j=0; j<n; j++)
{
int M_j0,M_j1;
MPI_Isend(&MLocal[i][0], 1, MPI_INT, j, my_rank+i*n+j+0, MPI_COMM_WORLD, &send_request0);
MPI_Isend(&MLocal[i][1], 1, MPI_INT, j, my_rank+i*n+j+1, MPI_COMM_WORLD, &send_request1);
MPI_Irecv(&M_j0, 1, MPI_INT, j, my_rank+i*n+j+0, MPI_COMM_WORLD, &recv_request0);
MPI_Irecv(&M_j1, 1, MPI_INT, j, my_rank+i*n+j+1, MPI_COMM_WORLD, &recv_request1);
//MPI_Wait(&send_request0, &status);
//MPI_Wait(&send_request1, &status);
MPI_Wait(&recv_request0, &status);
MPI_Wait(&recv_request1, &status);
// Do something ...
}
}
Затем с предложением GillesGouaillardet я изменил это 4 MPI_Isend
а также MPI_Irecv
чтобы:
MPI_Sendrecv(&MoatsLocal[i][0], 1, MPI_INT, j, my_rank+i*n+j+0, &M_j0,1, MPI_INT, my_rank, my_rank+i*n+j+0, MPI_COMM_WORLD, &status);
MPI_Sendrecv(&MoatsLocal[i][1], 1, MPI_INT, j, my_rank+i*n+j+1, &M_j1,1, MPI_INT, my_rank, my_rank+i*n+j+1, MPI_COMM_WORLD, &status);
Но тем не менее, это идет в бесконечный цикл.
ОБНОВИТЬ:
Я обновил код, часть проблемы была из-за ранжирования процессоров и соответствия тегов. Я исправил эту часть, но тем не менее, она имела склонность к тупику, что, я думаю, я знаю, в чем проблема. И, возможно, не сможет решить это. Если бы у меня было достаточное количество процессоров, чтобы распределить каждую строку к процессору, то есть n = p, проблем не было бы. Но проблема в том, где число процессоров меньше, чем n
тогда поток не проходит через главную диагональ. Я объясню это на примере, предположим, что у нас 4 процессора и n=6
, Предположим, что это распределение:
P0: |0 20 16 0 6 0 | |0 1|
P1: |20 0 0 19 0 6 | |1 1|
|16 0 0 0 12 0 | |2 1|
P2: |0 19 0 0 0 12| |3 1|
P3: |6 0 12 0 0 9 | |0 0|
|0 6 0 12 9 0 | |1 0|
Это то, что происходит через цикл.
Первая итерация:
P0 отправлять и получать в / из P1 информацию для (0,1): «20» и ждать (сделано)
P1 отправлять и получать в / из P0 информацию для (1,0): «20» и ждать (сделано)
P2 отправлять и получать в / из P1 информацию для (3,1): «19» и ждать
P3 отправлять и получать в / из P0 информацию для (4,1): «6» и ждать
Вторая итерация:
P0 отправлять и получать в / из P1 информацию для (0,2): «16» и ждать
P1 отправлять и получать в / из P2 информацию для (1,3): «19» и ждать (сделано)
P2 был в ожидании P1 (3,1): «19», затем просто получил его и сделал!
P3 ожидает P0 для (4,1): «6» и ждет
Третья итерация:
P0 ожидает P1 для (0,2): «16»
P1 отправлять и получать в / из P3 информацию для (1,5): «19» и ждать
P2 отправлять и получать в / из P3 информацию для (3,5): «12» и ждать
P3 ожидает P0 для (4,1): «6»
Четвертая итерация:
P0 ожидает P1 для (0,2): «16»
P1 ожидает P3 для (1,5): «19»
P2 ожидает P3 для (3,5): «12»
P3 ожидает P0 для (4,1): «6»
Теперь все ждут друг друга, я не думаю, что есть какой-то способ решить это. Решение, которое предложил ptb, может сработать, я попробую это.
Тем не менее, любая другая идея приветствуется!
Есть несколько проблем с кодом, который вы разместили
rows
, Однако в вашем описании строки распределены между процессорами, так что это, вероятно, ошибка.j=0
, MPI_Isend (…, j, …) означает, что каждый ранг будет отправлять что-то в корневой процесс. Затем следует вызов MPI_IRecv (…, j, …), MPI_Wait, что означает, что каждый процесс будет ожидать отправки от корневого процесса, который никогда не приходит.Проблема в том, что вам нужны ваши вызовы send и recv, чтобы соответствовать. Один из способов сделать это (не обязательно самый производительный) состоит в том, чтобы опубликовать все ваши посылки через MPI_Isend в цикле, а затем использовать MPI_Probe, MPI_Recv для обработки каждого ранга recvs (так как количество recvs — это количество посылок, которое вы точно знаете, как много). Пример псевдокода:
int send_count = 0;
for (int j=0; j<n; j++) {
if (matrix_entry[j] != 0) {
call MPI_Isend(M_local, 2, MPI_INT, j, 0, ...)
send_count++;
}
}
while (send_count) {
MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, comm, status)
/* get source from status and then call recv */
MPI_Recv(M_j01, 2, MPI_INTEGER, status(MPI_SOURCE), ...)
/* Do something with M_j01 */
send_count--;
}
Несколько небольших советов:
Вы всегда должны помнить, что каждый процесс независим. Нет синхронизации между процессами (ожидайте, если вы установите MPI_Barrier).
Я действительно не понимаю ваш цикл по строкам (действительно ли строки = 6?)
Тогда все процессы выполняют код ….
Это означает:
P0,1,2,3,4,5,6 Вызовы ваши sendrecv, все они делают это 6 раз, так как эти вызовы находятся в цикле …
Напоследок: какой будет обычный размер матрицы?
Это очень плохая идея — отправлять множество очень маленьких сообщений.
Вы должны разработать свой алгоритм следующим образом:
1) Определите, какие данные нужны PX процесса для обновления всех его столбцов.
2) осуществлять связь, которая собирает эти данные для всех процессов
3) Выполните обновление.