#c #performance #mpi #deadlock
#c #Производительность #mpi #тупиковая ситуация
Вопрос:
Я знаю, что существует множество вопросов и ответов о различных режимах отправки и получения MPI, но я считаю, что мой отличается, или я просто не могу применить эти ответы к моей проблеме.
В любом случае, мой сценарий выглядит следующим образом. Код предназначен для высокопроизводительных кластеров с потенциально тысячами ядер, организованных в многомерную сетку. В моем алгоритме есть две последовательные операции, которые необходимо выполнить, назовем их A и B, где A предшествует B. Краткое описание выглядит следующим образом:
О: каждый процессор имеет несколько буферов. Он должен отправлять каждый из этих буферов определенному набору процессоров. Для каждого отправляемого буфера набор принимающих процессоров может отличаться. Отправка является последним шагом операции A.
B: каждый процессор получает набор буферов от набора процессоров. Затем операция B будет работать с этими буферами, как только она получит их все. Результат этой операции будет сохранен в фиксированном месте (ни в буферах отправки, ни в буферах приема)
Также задаются следующие свойства:
- в A каждый процессор может вычислить, на какие процессоры отправлять, и он также может вычислить соответствующий тег в случае, если процессор получает несколько буферов от одного и того же отправляющего процессора (что очень вероятно).
- в B каждый процессор также может вычислить, от каких процессоров он будет получать, и соответствующие теги, с которыми были отправлены сообщения.
- Каждый процессор имеет свои собственные буферы отправки и буферы приема, и они не пересекаются (т. Е. Нет процессора, который также использует свой буфер отправки в качестве буфера приема, и наоборот).
- A и B выполняются в цикле среди других операций перед A и после B. Мы можем гарантировать, что буфер отправки не будет использоваться снова до следующей итерации цикла, где он заполняется новыми данными в A, и буферы приема также не будут использоваться снова до следующей итерации, где они используются для получения новых данных в операции B.
- Переход между A и B должен, по возможности, быть точкой синхронизации, т. Е. Мы хотим убедиться, что все процессоры входят в B одновременно
Для отправки и получения как A, так и B должны использовать (вложенные) циклы самостоятельно для отправки и получения разных буферов. Однако мы не можем делать никаких предположений о порядке этих операторов отправки и получения, т. Е. Для любых двух буферов buf0
, и buf1
мы не можем гарантировать, что если buf0
он получен каким-либо процессором ранее buf1
, он buf0
также был отправлен ранее buf1
. Обратите внимание, что на данный момент использование групповых операций, таких как MPI_Broadcast
etc., пока не является вариантом из-за сложности определения набора процессоров приема / отправки.
Вопрос: Какие режимы отправки и получения я должен использовать? Я прочитал много разных материалов об этих разных режимах, но я не могу по-настоящему разобраться в них. Наиболее важным свойством является свобода от взаимоблокировки, а следующая важная вещь — производительность. Я склоняюсь к использованию MPI_Isend()
в A без проверки статуса запроса и снова использую неблокирующее MPI_IRecv()
в циклах B, а затем использую MPI_Waitall()
, чтобы убедиться, что все буферы были получены (и, как следствие, также все буферы были отправлены и процессоры синхронизированы).
Это правильный подход, или я должен использовать буферизованные отправки или что-то совершенно другое? У меня нет большого опыта в MPI, и документация мне тоже не очень помогает.
Ответ №1:
Из того, как вы описываете свою проблему, я думаю MPI_Isend
, что это, вероятно, лучший (единственный?) Вариант для A, Потому что он гарантированно неблокирующий, тогда как MPI_Send
может быть неблокирующим, но только если он способен внутренне буферизировать ваш буфер отправки.
Затем вы должны иметь возможность использовать an MPI_Barrier
, чтобы все процессы вводили B одновременно. Но это может привести к снижению производительности. Если вы не настаиваете на том, чтобы все процессы вводили B одновременно, некоторые могут начать получать сообщения раньше. Кроме того, учитывая ваши непересекающиеся буферы отправки и приема, это должно быть безопасно.
Для B вы можете использовать MPI_Irecv
или MPI_Recv
. MPI_Irecv
вероятно, будет быстрее, потому что стандарт MPI_Recv
может ожидать более медленной отправки от другого процесса.
Независимо от того, блокируете вы на принимающей стороне или нет, вы должны вызвать MPI_Waitall
перед завершением цикла, чтобы убедиться, что все операции отправки / recv завершены успешно.
Дополнительный момент: вы можете использовать MPI_ANY_SOURCE
с MPI_Recv
, чтобы получать сообщения блокирующим способом и немедленно обрабатывать их, независимо от того, в каком порядке они поступают. Однако, учитывая, что вы указали, что никакие операции с данными не выполняются до тех пор, пока не будут получены все данные, это может оказаться не столь полезным.
Наконец: как упоминалось в этих рекомендациях, вы получите максимальную производительность, если сможете реструктурировать свой код так, чтобы его можно было просто использовать MPI_SSend
. В этом случае вы вообще избегаете какой-либо буферизации. Для достижения этого вам нужно, чтобы все процессы сначала вызывали an MPI_Irecv
, а затем начинали отправку via MPI_Ssend
. Возможно, провести рефакторинг таким образом не так сложно, как вы думаете, особенно если, как вы говорите, каждый процесс может самостоятельно определять, какие сообщения он будет получать от кого.
Комментарии:
1. Вы имели в виду
MPI_Rsend()
?2. Большое спасибо, все оказалось именно так, как вы сказали. Мы начали с использования неблокирующего, а теперь переработали весь код, чтобы использовать эту
MPI_Issend()
функцию в сочетании сMPI_Irecv()
ранее опубликованным соответствием, и это работает как шарм.