Очевидная ситуация взаимоблокировки не возникает

#mpi #deadlock

#mpi #взаимоблокировка

Вопрос:

Я полностью застрял. Этот код

 MPI_Comm_rank(MPI_COMM_WORLD, amp;myrank);
if(myrank==0) i=1;
if(myrank==1) i=0;
MPI_Send(sendbuf, 1, MPI_INT, i, 99, MPI_COMM_WORLD);
MPI_Recv(recvbuf, 1, MPI_INT, i, 99, MPI_COMM_WORLD,amp;status);
  

работает над двумя процессами. Почему нет взаимоблокировки?
То же самое с неблокирующей версией

 MPI_Comm_rank(MPI_COMM_WORLD, amp;myrank);
if(myrank==0) i=1;
if(myrank==1) i=0;
MPI_Isend(sendbuf, 1, MPI_INT, i, 99, MPI_COMM_WORLD,amp;req);
MPI_Wait(amp;req, amp;status);
MPI_Irecv(recvbuf, 1, MPI_INT, i, 99, MPI_COMM_WORLD,amp;req);
MPI_Wait(amp;req, amp;status);
  

Логически должна быть блокировка, но это не так. Почему?
Как мне принудительно заблокировать MPI?

Комментарии:

1. «застрял при отсутствии взаимоблокировки» очень интригует. MPI_Send() возвращает, когда буфер отправки может быть использован повторно, и это может произойти до отправки сообщения и даже до публикации получения. В этом случае имеется только одно очень маленькое сообщение, поэтому оно, вероятно, отправляется в режиме ожидания и MPI_Send() немедленно возвращается. Не думайте, что это всегда так. Если вы хотите принудительно заблокировать, вы можете использовать MPI_Ssend() или настроить конфигурацию MPI, чтобы отключить отправку в режиме ожидания.

2. Точно, я хочу принудительно заблокировать стандартный режим отправки. Не могли бы вы посоветовать, как настроить конфигурацию MPI, чтобы отключить отправку в режиме ожидания?

3. Это зависит от вашей библиотеки MPI (поставщика и версии). Выберите легкий путь и просто перекомпилируйте с -DMPI_Send=MPI_Ssend помощью .

Ответ №1:

MPI имеет два вида операций «точка-точка» — блокирующие и неблокирующие. Поначалу это может немного сбить с толку, особенно когда дело доходит до отправки сообщений, но поведение блокировки не связано с операцией физической передачи данных. Это скорее относится к периоду времени, в течение которого MPI все еще может обращаться к буферу (либо отправлять, либо получать), и поэтому приложение не должно изменять его содержимое (с помощью операций отправки) или все еще может считывать старый мусор (с помощью операций чтения).

Когда вы выполняете блокирующий вызов MPI, он возвращается только после завершения использования буфера отправки или приема. При операциях приема это означает, что сообщение было получено и сохранено в буфере, но при операциях отправки все сложнее. Существует несколько режимов для операции отправки:

  • буферизованный — данные отправляются не сразу, а копируются в локальный пользовательский буфер, и вызов возвращается сразу после этого. Фактическая передача сообщения происходит в какой-то момент в будущем, когда MPI решит это сделать. Существует специальный вызов MPI, MPI_Bsend , который всегда ведет себя таким образом. Существуют также вызовы для присоединения и отсоединения предоставленного пользователем буфера.
  • синхронный — MPI ожидает, пока не будет опубликована соответствующая операция приема и не начнется передача данных. Он возвращается, когда все сообщение находится в пути, и буфер отправки свободен для повторного использования. Существует специальный вызов MPI, MPI_Ssend , который всегда ведет себя таким образом.
  • готово — MPI пытается отправить сообщение, и это удается только в том случае, если операция приема уже была опубликована. Идея здесь состоит в том, чтобы пропустить рукопожатие между рядами, что может уменьшить задержку, но не указано, что именно произойдет, если получатель не готов. Для этого существует специальный вызов, MPI_Rsend , но рекомендуется не использовать его, если вы действительно не знаете, что делаете.

MPI_Send вызывает так называемый стандартный режим отправки, который может представлять собой любую комбинацию синхронного и буферизованного режимов, причем последний использует буфер, предоставляемый системой MPI, а не предоставляемый пользователем. Фактические детали оставлены для реализации MPI и, следовательно, сильно отличаются.

Чаще всего небольшие сообщения буферизуются, в то время как большие сообщения отправляются синхронно, что вы и наблюдаете в вашем случае, но никогда не следует полагаться на такое поведение, и определение «small» зависит от реализации и типа сети. Правильная программа MPI не перейдет в взаимоблокировку, если все MPI_Send вызовы будут заменены на MPI_Ssend , что означает, что вы никогда не должны предполагать, что небольшие сообщения буферизуются. Но правильная программа также не будет ожидать MPI_Send синхронности для больших сообщений и полагаться на это поведение для синхронизации между рангами, т. Е. Замена MPI_Send MPI_Bsend и предоставление достаточно большого буфера не должны изменять поведение программы.

Существует очень простое решение, которое всегда работает, и оно освобождает вас от необходимости помнить, что нельзя полагаться на какие-либо предположения — просто используйте MPI_Sendrecv . Это комбинированная операция отправки и получения, которая никогда не приводит к взаимоблокировке, за исключением случаев, когда операция отправки или получения (или обе) не имеет аналогов. С помощью send-receive ваш код будет выглядеть следующим образом:

 MPI_Comm_rank(MPI_COMM_WORLD, amp;myrank);
if (myrank==0) i=1;
if (myrank==1) i=0;
MPI_Sendrecv(sendbuf, 1, MPI_INT, i, 99,
             recvbuf, 1, MPI_INT, i, 99, MPI_COMM_WORLD, amp;status);
  

Комментарии:

1. Это означает, что моя программа может быть заблокирована или не заблокирована в зависимости от некоторых настроек и размера буфера. Это меня совсем не устраивает. Есть ли какой-нибудь надежный способ объективно определить, возникают ли блокировки при использовании MPI_Send?

2. Единственный надежный способ вызвать взаимоблокировку с MPI_Send помощью — это присвоить ему псевдоним MPI_Ssend , либо используя магию компоновщика, либо затеняя символ библиотеки вашей собственной функцией, которая вызывает MPI_Ssend . Все остальное требует изменений во время выполнения MPI, а в некоторых реализациях это может быть вообще невозможно. Универсального переключателя нет. Добавьте в свой вопрос подробную информацию о вашей конкретной реализации MPI, и кто-нибудь может рассказать вам, как ее настроить.

3. ОК. Что касается неблокирующей функции? Я добавил приведенный выше пример, который againg не хочет блокироваться.

4. MPI_Isend за которым сразу MPI_Wait следует избыточно, поскольку это эквивалентно MPI_Send . Если MPI_Send буферизуется, то же самое относится и к комбинации MPI_Isend и MPI_Wait . Если вы хотите, чтобы комбинация блокировалась, используйте MPI_Issend followed by MPI_Wait .