Почему MPI_Waitall возвращается до того, как MPI_Irecv получит необходимые данные?
Я решаю уравнение Лапласа с неструктурированной сеткой, используя MPI. Я планирую сначала завершить отправку и получение данных с соседнего раздела, а затем выполнить расчеты для каждого процессора. MPI_Waitall
используется для ожидания всех MPI_Isend()
а также MPI_Irecv()
отделка, но проблема все процессор передать MPI_Waitall
и застрял там при чтении полученных данных буфера, потому что каждый процессор фактически не получил никаких данных (флаг MPI_Testall возвращает 0). В моем понимании MPI_Irecv
должен был получить данные раньше MPI_Waitall
возвращается.
double **sbuf = calloc(partition->ptn_nnbr[my_id], sizeof(double *));
double **rbuf = calloc(partition->ptn_nnbr[my_id], sizeof(double *));
for (i = 0; i < partition->ptn_nnbr[my_id]; i++)
{
//rbuf[i] = calloc(partition->ptn_cnt[my_id][k1], sizeof(double));
rbuf[i] = calloc(MAX_nnode, sizeof(double));
sbuf[i] = calloc(MAX_nnode, sizeof(double));
}
nrm = 1; // nrm = max(abs(r[i])), i = 1..n
iter = 0;
printf("Entering jacobi; iterations = %d, error norm = %e\n", iter, nrm);
while (nrm > TOL && iter<4 ){
init_boundary_conditions_ptn(x_ptn, mesh, my_id, partition);
iter++;
int req_idx= 0;
int idx = 0;
MPI_Request *request = (MPI_Request *) calloc(2 * partition->ptn_nnbr[my_id], sizeof(MPI_Request));
MPI_Status *status = calloc(2 * partition->ptn_nnbr[my_id], sizeof(MPI_Status));
int *flag = calloc(2 * partition->ptn_nnbr[my_id], sizeof(int));
for (k1 = 0; k1 < partition->nptn; k1++)
{
if (partition->ptn_list[my_id][k1] != NULL)
{
for (i = 0; i < partition->ptn_cnt[k1][my_id]; i++)
{
sbuf[idx][i] = x_ptn->val[partition->ptn_list[k1][my_id][i] - partition->ptn[my_id] + 1];
}
MPI_Isend(sbuf[idx], partition->ptn_cnt[k1][my_id], MPI_DOUBLE, k1, TAG, MPI_COMM_WORLD, &request[req_idx]);
//printf("isend done from nbr %d for partition %d \n", k1, my_id);
req_idx++;
idx++;
}
}
idx = 0;
for (k1 = 0; k1 < partition->nptn; k1++)
{
if (partition->ptn_list[my_id][k1] != NULL)
{
MPI_Irecv(rbuf[idx], partition->ptn_cnt[my_id][k1], MPI_DOUBLE, k1, TAG, MPI_COMM_WORLD, &request[req_idx]);
//printf("irecv done from nbr %d for partition %d \n", k1, my_id);
req_idx++;
idx++;
}
}
printf("partition %d is waiting \n", my_id);
MPI_Testall(2 * partition->ptn_nnbr[my_id],request,flag, status);
for (i = 0; i < 2 * partition->ptn_nnbr[my_id]; i++)
{
printf("flag[%d] is %d from partition %d\n", i, flag[i], my_id);
}
MPI_Waitall(2 * partition->ptn_nnbr[my_id], request, status);
printf("partition %d pass MPI_Wait \n", my_id);
for (k1 = 0; k1 < partition->nptn; k1++)
{
if (partition->ptn_list[my_id][k1] != NULL)
{
MPI_Probe(k1, TAG, MPI_COMM_WORLD, status1);
MPI_Get_count(status1, MPI_DOUBLE, &count);
printf("count is %d from nbr %d \n", count, k1);
for (i = 0; i < count; i++)
{
x->val[partition->ptn_list[my_id][k1][i]] = rbuf[idx][i];
}
}
}
//printf("exchange complete from partition %d\n", my_id);
jacobi_step_csr_matrix(A_ptn, x, b_ptn, y_ptn); // y = inv(D)*(b + (D-A)*x), D = diag(A)
copy_vector(y_ptn, x_ptn);
MPI_Gatherv(x_ptn->val, x_ptn->n, MPI_DOUBLE, x->val, x_count, x_dis, MPI_DOUBLE,0, MPI_COMM_WORLD);
if (my_id == 0)
{
init_boundary_conditions(x, mesh, partition->perm);
matvec_csr_matrix(A, x, r); // r = A*x
sxapy(b, -1.0, r); // r = b - r
zero_boundary_conditions(r, mesh, partition->perm);
nrm = norm_inf(r);
}
MPI_Bcast(&nrm, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
printf("nrm is %f from partition %d in iter %d \n", nrm, my_id, iter);
free(request);
free(status);
Выход:
Processor 0 start Jacobi
MAx_node is 2 from partition 0
Entering jacobi; iterations = 0, error norm = 1.000000e+00
Processor 2 start Jacobi
MAx_node is 2 from partition 2
Entering jacobi; iterations = 0, error norm = 1.000000e+00
Processor 3 start Jacobi
MAx_node is 2 from partition 3
Entering jacobi; iterations = 0, error norm = 1.000000e+00
Processor 1 start Jacobi
MAx_node is 2 from partition 1
Entering jacobi; iterations = 0, error norm = 1.000000e+00
partition 3 is waiting
flag[0] is 0 from partition 3
flag[1] is 0 from partition 3
flag[2] is 0 from partition 3
flag[3] is 0 from partition 3
partition 3 pass MPI_Wait
partition 0 is waiting
flag[0] is 0 from partition 0
flag[1] is 0 from partition 0
flag[2] is 0 from partition 0
flag[3] is 0 from partition 0
partition 0 pass MPI_Wait
partition 2 is waiting
flag[0] is 0 from partition 2
flag[1] is 0 from partition 2
flag[2] is 0 from partition 2
flag[3] is 0 from partition 2
partition 2 pass MPI_Wait
partition 1 is waiting
flag[0] is 0 from partition 1
flag[1] is 0 from partition 1
flag[2] is 0 from partition 1
flag[3] is 0 from partition 1
partition 1 pass MPI_Wait
1 ответ
Мне кажется, что ваше понимание неблокирующей коммуникации в MPI несколько расплывчато. Прежде всего, вы используете неправильный тестовый вызов. MPI_Testall
выводит скалярный флаг завершения, который указывает, завершены ли все запросы к моменту вызова MPI_Testall
сделан. Если бы вы использовали MPI_Testsome
вместо этого вы бы заметили, что только некоторые запросы (или, скорее всего, ни один) будут выполнены. Стандарт MPI позволяет отложить выполнение операций, не связанных с блокировкой, и только в определенных случаях. Завершение гарантируется только:
- после звонка
MPI_Wait{all|some|any}
(который просто не возвращается до завершения запросов); - после
MPI_Test{all|some|any}
возвращает истинный флаг завершения. Нет гарантии, что один звонокMPI_Test...
приведет к завершению - функции теста должны вызываться многократно, пока флаг не укажет на завершение запросов.
По соображениям производительности большинство библиотек MPI являются однопоточными, то есть нет фонового потока, который обрабатывает неблокирующие вызовы, за исключением некоторых конкретных архитектур, которые реализуют прогресс в оборудовании. Поэтому периодические вызовы в библиотеку MPI необходимы для того, чтобы фактически происходила неблокирующая связь, и вы ожидаете, что все неблокирующие запросы должны быть выполнены к тому времени, когда вы звоните MPI_Testall
это просто неправильно.
Кроме того, ваша программа застревает в MPI_Probe
, Это блокирующий вызов, который должен вызываться до получения сообщения, а не после. Сообщение уже получено MPI_Irecv
и пробный вызов ожидает другое сообщение, которое никогда не приходит. Не звони MPI_Probe
, Передайте соответствующий элемент status
массив для MPI_Get_count
вместо.
В качестве последнего замечания, вы проходите 2 * partition->ptn_nnbr[my_id]
как количество запросов. Убедитесь, что это значение действительно соответствует значению, накопленному в req_idx
иначе ваша программа вылетит. Неактивные запросы должны быть установлены в MPI_REQUEST_NULL
и ни Open MPI, ни MPICH не использует NULL
(как установлено в вашем случае при вызове calloc(3)
) для неактивных запросов. Вы должны пройти req_idx
как количество запросов вместо.