Гарантия безопасности для асинхронных отправлений в MPI

0

В моем приложении я использую MPI для распределения заданий в режиме "ведущий-ведомый". Работа подчиняется рабочему, а затем собираются результаты. В многопоточной версии этой программы существовал потенциальный тупик, когда все процессоры одновременно пытаются Send (блокировать), потому что не было соответствующего Recv. Я придумал решение, которое, похоже, работает, но я хотел бы получить гарантию (кроме того, проверив ее еще десять тысяч раз).

Безопасность моей программы является несомненной, если этот маленький код гарантированно - при условии соответствующей реализации. (очевидно, это работает только для двух процессоров и не предназначено для большего):

#include <cassert>
#include "mpi.h"

int main()
{
   MPI::Init();
   int ns[] = {-1, -1};
   int rank = MPI::COMM_WORLD.Get_rank();
   ns[rank] = rank;
   MPI::Request request = MPI::COMM_WORLD.Isend(&rank, sizeof(int), MPI::BYTE, 1 - rank, 0);
   MPI::COMM_WORLD.Recv(&ns[1 - rank], sizeof(int), MPI::BYTE, 1 - rank, 0);
   request.Wait();
   assert( ns[0] == 0 );
   assert( ns[1] == 1 );
   MPI::Finalize();
}

Так что мой вопрос: Является ли разделительный из Isend с Recv, пока я не позову Wait на Request возвращенного Isend хорошо определенно безопасная вещь в MPI?

(Отказ от ответственности: этот фрагмент кода не предназначен для исключительной безопасности или особенно красив. Он предназначен только для демонстрационных целей)

Теги:
mpi

2 ответа

1
Лучший ответ

Ваш код абсолютно безопасен. Это гарантируется семантикой неблокирующих операций, определенных в стандарте MPI §3.7.4. - Семантика неблокирующих коммуникаций:

Прогресс. Вызов MPI_WAIT который завершает прием, в конце концов прекратится и вернется, если начата соответствующая отправка, если только передача не будет выполнена другим приемом. В частности, если сопоставление отправления не блокируется, то прием должен завершиться, даже если вызов не отправлен отправителем для завершения отправки. Аналогично, вызов MPI_WAIT который завершает отправку, в конце концов будет возвращен, если MPI_WAIT совпадение будет запущено, если только прием не будет удовлетворен другой отправкой, и даже если для завершения приема не будет выполнен вызов.

Операция блокировки в этом контексте эквивалентна инициализации неблокирующей, сразу же после которой происходит ожидание.

Если слова стандарта недостаточно обнадеживают, этот раздел кода из реализации MPI_SENDRECV в Open MPI может помочь:

if (source != MPI_PROC_NULL) { /* post recv */
    rc = MCA_PML_CALL(irecv(recvbuf, recvcount, recvtype,
                            source, recvtag, comm, &req));
    OMPI_ERRHANDLER_CHECK(rc, comm, rc, FUNC_NAME);
}

if (dest != MPI_PROC_NULL) { /* send */
    rc = MCA_PML_CALL(send(sendbuf, sendcount, sendtype, dest,
                           sendtag, MCA_PML_BASE_SEND_STANDARD, comm));
    OMPI_ERRHANDLER_CHECK(rc, comm, rc, FUNC_NAME);
}

if (source != MPI_PROC_NULL) { /* wait for recv */
    rc = ompi_request_wait(&req, status);
} else {
    if (MPI_STATUS_IGNORE != status) {
        *status = ompi_request_empty.req_status;
    }
    rc = MPI_SUCCESS;
}

Не имеет значения, используете ли вы Irecv/Send/Wait(receive) или Isend/Recv/Wait(send) - оба одинаково безопасны, когда дело доходит до возможных взаимоблокировок. Разумеется, взаимоблокировки могут (и будут) возникать, если чередующаяся операция неправильно согласована.

Единственное, что приводит к несоответствию вашего кода, это тот факт, что он использует привязки MPI C++. Они были устаревшими в MPI-2.2 и удалены в MPI-3.0. Вместо этого вы должны использовать API C.

  • 0
    Как вы гарантируете, что Wait вызывается в примере кода? Оба процесса вызывают блокировку Recv перед ожиданием.
  • 0
    Либо поток фоновой последовательности заботится о выполнении неблокирующей операции, либо (в однопоточных реализациях) блокирующий прием также обрабатывает неблокирующую передачу.
-2

Этот код не гарантированно работает. Реализации MPI могут не делать ничего, связанные с вашим Isend пока вы не назовете соответствующую функцию Wait.

Лучший вариант - сделать неблокирующими функции Send и Recv. Вы должны использовать Isend и Irecv и вместо использования Wait вы будете использовать Waitall. Функция Waitall принимает массив запросов MPI и ждет их всех (одновременно делая прогресс на каждом).

Таким образом, ваша программа будет выглядеть так:

#include <cassert>
#include "mpi.h"

int main()
{
   MPI::Init();
   int ns[] = {-1, -1};
   int rank = MPI::COMM_WORLD.Get_rank();
   MPI::Request requests[2];
   ns[rank] = rank;
   requests[0] = MPI::COMM_WORLD.Isend(&rank, sizeof(int), MPI::BYTE, 1 - rank, 0);
   requests[1] = MPI::COMM_WORLD.Irecv(&ns[1 - rank], sizeof(int), MPI::BYTE, 1 - rank, 0);
   MPI::Request::Waitall(2, requests)
   assert( ns[0] == 0 );
   assert( ns[1] == 1 );
   MPI::Finalize();
}
  • 0
    Интересно. Но это кажется мне странным: похоже, что все в порядке, если я Isend несколько Isend / IRecv а затем просто подожду их всех в конце. Это действительно безопасно?
  • 0
    Единственное, что может сделать его небезопасным, - это данные. Вы не можете изменить буфер, который вы передаете в функцию Send пока вы не закончили Wait . Точно так же вы не можете читать из буфера, который вы передаете в Recv пока вы не закончите Wait .
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню