Россия, Новосибирск, НГПУ, 2009 |
Основы программирования на MPI
2.3. Методы передачи данных в MPI: "точка-точка" и радиовещательные (broadcast) сообщения
Операции передачи данных в MPI типа "точка-точка" представляют собой передачу сообщений между, в точности, двумя MPI-процессами. Один процесс, при этом, выполняет команду Send (послать), тогда как другой процесс выполняет команду Receive (принять).
Выполнение команд Send и Receive осуществляется посредством вызова соответствующих MPI-функций, которые имеют различные типы, или, другими словами, различное назначение:
- Блокирующий Send / блокирующий Receive
- Неблокирующий Send / неблокирующий Receive
- Синхронный Send
- Буферированный Send
- Комбинированный Send / Receive
- Send по готовности ( "ready" Send ).
С любым типом операции Send может состоять в паре любой тип операции Receive.
Функции передачи данных типа "точка-точка" имеют список аргументов одного из следующих форматов:
- Блокирующий Send:
MPI_Send ( buffer, count, type, dest, tag, comm )
- Неблокирующий Send:
MPI_Isend ( buffer, count, type, dest, tag, comm, request )
- Блокирующий Receive:
MPI_Recv ( buffer, count, type, source, tag, comm., status )
- Неблокирующий Receive:
MPI_Irecv ( buffer, count, type, source, tag, comm., request )
Аргументы в этих функциях имеют следующее назначение:
- buffer - место хранения данных, которые посылаются или принимаются;
- count - количество элементов данных конкретного типа, которые посылаются или принимаются;
- type - тип элементарных данных, задаваемый через встроенные MPI-типы, такие как (для языка С): MPI_CHAR, MPI_SHORT, MPI_INT, MPI_LONG, MPI_FLOAT, MPI_DOUBLE, MPI_BYTE, MPI_PACKED и др.
- dest - указывает процесс, которому должно быть доставлено сообщение - задается через ранг принимающего процесса;
- source - аргумент функций приема сообщений, указывающий номер посылающего процесса; указание значения MPI_ANY_SOURCE означает прием сообщения от любого процесса;
- tag - произвольное неотрицательное целое число, присваиваемое программистом для однозначной идентификации сообщения; у парных операций Send и Reсeive эти числа должны совпадать; указание у операции Receive значения MPI_ANY_TAG может быть использовано для приема любого сообщения, независимо от значения tag ;
- comm - указывает на коммуникатор, в рамках которого трактуются значения аргументов dest и source ; чаще всего используется встроенный коммуникатор MPI_COMM_WORLD ;
- status - для операции Receive, указывает источник (source) сообщения и его тег ( tag ); в языке С, этот аргумент есть указатель на встроенную структуру MPI_Status ; из этой же структуры может быть получено количество принятых байт посредством функции MPI_Get_count ;
- request - используется в неблокирующих операциях Send и Receive, и задает уникальный "номер запроса"; в языке С, этот аргумент является указателем на встроенную структуру MPI_Request.
К наиболее часто используемым блокирующим функциям передачи сообщений относятся следующие функции:
MPI_Send
Базовая блокирующая операция посылки сообщения. Заканчивает свою работу только тогда, когда программный буфер, из которого берутся данные для посылки, готов для повторного использования.
Формат вызова:
MPI_Send ( &buf, count, datatype, dest, tag, comm. )
MPI_Recv
Принимает сообщения и блокирует вызывающий эту функцию процесс до тех пор, пока в программном буфере не станут доступными принятые данные.
Формат вызова:
MPI_Recv ( &buf, count, datatype, source, tag, comm, &status )
MPI_Ssend
Синхронная блокирующая операция посылки сообщения: посылает сообщение и блокирует вызвавший эту функцию процесс, пока программный буфер не будет готов к повторному использованию и пока процесс-получатель не начал принимать посылаемые сообщения.
Формат вызова:
MPI_Ssend ( &buf, count, datatype, dest, tag, comm )
MPI_Bsend, MPI_Buffer_attach
Перед вызовом MPI_BSend, программист должен вызвать функцию MPI_Buffer_attach для размещения буфера, используемого в MPI_Bsend. Буферированная блокирующая операция посылки сообщения заканчивает свою работу, когда данные из программного буфера скопированы в буфер посылки.
Форматы вызовов:
MPI_Buffer_attach ( &buffer, size ) MPI_Bsend ( &buf, count, datatype, dest, tag, comm )
В нижеследующем примере, процесс 0 посылает однобайтовое сообщение процессу 1, и ждет от него аналогичного сообщения.
Основные особенности и отличия радиовещательных (коллективных) обменов данными от обменов типа "точка-точка" состоят в следующем:
- принимают и/или передают данные одновременно все процессы группы для указываемого коммуникатора;
- радиовещательная (коллективная) функция выполняет одновременно и прием, и передачу данных, а потому она имеет параметры, часть из которых относится к приему, а часть - к передаче данных;
- как правило, значения всех параметров (за исключением адресов буферов) должны быть идентичны во всех процессах;
- коллективные операции являются блокирующими;
- коллективные операции могут использоваться только для встроенных (predefined) MPI-типов данных, но не могут использоваться для производных (derived) MPI-типов данных.
MPI_Bcast
Посылает сообщение от процесса с рангом "root" (обычно, это процесс с рангом 0) всем другим процессам в группе.
Формат вызова:
MPI_Bcast ( &buffer, count, datatype, root, comm )
MPI_Gather
Собирает сообщения от каждого из процессов в группе в приемный буфер процесса с рангом 'root".
Формат вызова:
MPI_Gather ( &sendbuf, sendcount, sendtype, &recvbuf, recvcount, recvtype, root, comm )
Cледует заметить, что
- sendtype и recvtype, в общем случае, могут различаться, а потому будут задавать разную интерпретацию данных на приемной и передающей стороне;
- процесс root также отправляет данные, но в свой же приемный буфер.
MPI_Scatter
Эта функция является обратной к функции MPI_Gather: отдельные части передающего буфера процесса с рангом 'root& распределяются по приемным буферам всех других процессов в группе.
Формат вызова:
MPI_Scatter ( &sendbuf, sendcount, sendtype, &recvbuf, recvcount, recvtype, root, comm )
MPI_Allgather
Эта функция аналогична функции MPI_Gather, за исключением того, что прием данных осуществляет не один процесс, а все процессы: каждый процесс имеет специфическое содержимое в передающем буфере, но все процессы получают в итоге одинаковое содержимое в приемном буфере.
Формат вызова:
MPI_Allgather ( &sendbuf, sendcount, sendtype, &recvbuf, recvcount, recvtype, comm )
MPI_Alltoall
Каждый процесс отдельные части своего передающего буфера рассылает всем остальным процессам; каждый процесс получает эти части от всех остальных и размещает их по порядку рангов процессов, от которых они получены.
Формат вызова:
MPI_Alltoall ( &sendbuf, sendcount, sendtype, &recvbuf, recvcount, recvtype, comm )
В нижеследующем примере, с помощью функции MPI_Scatter строки массива рассылаются отдельным процессам:
Вывод на консоль данной программы будет таким:
rank= 0 Results: 1.000000 2.000000 3.000000 4.000000 rank= 1 Results: 5.000000 6.000000 7.000000 8.000000 rank= 2 Results: 9.000000 10.000000 11.000000 12.000000 rank= 3 Results: 13.000000 14.000000 15.000000 16.000000