К операциям этого типа относятся две представленные в предыдущем разделе коммуникационные процедуры. В коммуникационных операциях типа точка-точка всегда участвуют не более двух процессов: передающий и принимающий. В MPI имеется множество функций, реализующих такой тип обменов. Многообразие объясняется возможностью организации таких обменов множеством способов. Описанные в предыдущем разделе функции реализуют стандартный режим с блокировкой.
Блокирующие функции подразумевают выход из них только после полного окончания операции, т.е. вызывающий процесс блокируется, пока операция передачи или приема данных не будет завершена.
Неблокирующие функции подразумевают совмещение операций обмена с другими операциями, поэтому неблокирующие функции передачи и приема по сути дела являются функциями инициализации соответствующих операций. Для опроса завершенности операции (и завершения) вводятся дополнительные функции.
Как для блокирующих, так и неблокирующих операций MPI поддерживает четыре режима выполнения. Эти режимы касаются только функций передачи данных, поэтому для блокирующих и неблокирующих операций имеется по четыре функции посылки сообщения. Ниже перечислены имена базовых коммуникационных функций типа точка-точка, имеющихся в библиотеке MPI.
Режимы выполнения | С блокировкой | Без блокировки |
Стандартная посылка | MPI_Send | MPI_ISend |
Синхронная посылка | MPI_SSend | MPI_ISSend |
Буферизованная посылка | MPI_BSend | MPI_IBSend |
Согласованная посылка | MPI_RSend | MPI_IRsend |
Прием информации | MPI_Recv | MPI_IRecv |
Из таблицы хорошо виден принцип формирования имен функций. К именам базовых функций Send/Recv добавляются различные префиксы.
- Префикс S (synchronous) -
- означает синхронный режим передачи данных. Операция передачи данных заканчивается только тогда, когда заканчивается прием данных. Функция нелокальная.
- Префикс B (buffered) -
- означает буферизованный режим передачи данных. В адресном пространстве передающего процесса с помощью специальной функции создается буфер обмена, который используется в операциях обмена. Операция посылки заканчивается, когда данные помещены в этот буфер. Функция имеет локальный характер.
- Префикс R (ready) -
- согласованный или подготовленный режим передачи данных. Операция передачи данных начинается только тогда, когда принимающий процессор выставил признак готовности приема данных, инициировав операцию приема. Функция нелокальная.
- Префикс I (immediate) -
- относится к неблокирующим операциям.
Все функции передачи и приема сообщений могут использоваться в любой комбинации друг с другом. Функции передачи, находящиеся в одном столбце, имеют совершенно одинаковый синтаксис и отличаются только внутренней реализацией. Поэтому в дальнейшем будем рассматривать только стандартный режим, который в обязательном порядке поддерживают все реализации MPI.
В стандартном режиме выполнение операции обмена включает три этапа:
- Передающая сторона формирует пакет сообщения. Этот пакет передается отправителем в системный буфер, и на этом функция посылки сообщения заканчивается.
- Сообщение системными средствами передается адресату.
- Принимающий процессор извлекает сообщение из системного буфера, когда у него появится потребность в этих данных. Содержательная часть сообщения помещается в адресное пространство принимающего процесса (параметр buf), а служебная - в параметр status.
Приведем пример программы обмена данными, выполняемой на двух процессорах с использование блокирующих функций.
C определяем свой идентификатор процесса CALL MPI_COMM_RANK(comm, rank, ierr) IF (rank.EQ.0) THEN C если это первый процесс, то сначала посылаем данные, ждем завершения операции, потом принимаем CALL MPI_SEND(sendbuf, count, MPI_REAL, 1, tag, comm, ierr) CALL MPI_RECV(recvbuf, count, MPI_REAL, 1, tag, comm, status, ierr) ELSE IF (rank.EQ.1) THEN C если это второй процесс, то сначала принимаем данные, ждем завершения операции, потом отсылаем CALL MPI_RECV(recvbuf, count, MPI_REAL, 0, tag, comm, status, ierr) CALL MPI_SEND(sendbuf, count, MPI_REAL, 0, tag, comm, ierr) END IF
В ситуациях, когда требуется выполнить взаимный обмен данными между процессами, безопаснее использовать совмещенную операцию MPI_Sendrecv.
MPI_SENDRECV(SENDBUF, SENDCOUNT, SENDTYPE, DEST, SENDTAG, RECVBUF, RECVCOUNT, RECVTYPE, SOURCE, RECVTAG, COMM, STATUS, IERROR) <type> SENDBUF(*), RECVBUF(*) INTEGER SENDCOUNT, SENDTYPE, DEST, SENDTAG, RECVCOUNT, RECVTYPE, SOURCE, RECV TAG, COMM, STATUS(MPI_STATUS_SIZE), IERROR
Тип | Параметр | Описание |
IN | sendbuf | адрес начала расположения посылаемого сообщения |
IN | sendcount | число посылаемых элементов |
IN | sendtype | тип посылаемых элементов |
IN | dest | номер процесса-получателя |
IN | sendtag | идентификатор посылаемого сообщения |
OUT | recvbuf | адрес начала расположения принимаемого сообщения |
IN | recvcount | максимальное число принимаемых элементов |
IN | recvtype | тип элементов принимаемого сообщения |
IN | source | номер процесса-отправителя |
IN | recvtag | идентификатор принимаемого сообщения |
IN | comm | коммуникатор области связи |
OUT | status | атрибуты принятого сообщения |
Функция MPI_Sendrecv совмещает выполнение операций передачи и приема. Обе операции используют один и тот же коммуникатор, но идентификаторы сообщений могут различаться. Расположение в адресном пространстве процесса принимаемых и передаваемых данных не должно пересекаться. Пересылаемые данные могут быть различного типа и иметь разную длину.
Неблокирующие коммуникационные операции
Использование неблокирующих коммуникационных операций повышает безопасность с точки зрения возникновения тупиковых ситуаций, а также может увеличить скорость работы программы за счет совмещения выполнения вычислительных и коммуникационных операций. Эти задачи решаются разделением коммуникационных операций на две стадии: инициирование операции и проверку завершения операции.
Функция передачи сообщения без блокировки MPI_Isend
MPI_ISEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR) <type> BUF(*) INTEGER COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR
Тип | Параметр | Описание |
IN | buf | адрес начала расположения передаваемых данных |
IN | count | число посылаемых элементов |
IN | datatype | тип посылаемых элементов |
IN | dest | номер процесса-получателя |
IN | tag | идентификатор сообщения |
IN | comm | коммуникатор |
OUT | request | "запрос обмена" |
Возврат из подпрограммы происходит немедленно (immediate), без ожидания окончания передачи данных. Этим объясняется префикс I в именах функций. Поэтому переменную buf повторно использовать нельзя до тех пор, пока не будет погашен "запрос обмена". Это можно сделать с помощью подпрограмм MPI_Wait или MPI_Test, передав им параметр request.
Функция приема сообщения без блокировки MPI_Irecv
MPI_IRECV(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, REQUEST, IERROR) <type> BUF(*) INTEGER COUNT, DATATYPE, SOURCE, TAG, COMM, REQUEST, IERROR
Тип | Параметр | Описание |
---|---|---|
OUT | buf | адрес для принимаемых данных |
IN | count | максимальное число принимаемых элементов |
IN | datatype | тип элементов принимаемого сообщения |
IN | source | номер процесса-отправителя |
IN | tag | идентификатор сообщения |
IN | comm | коммуникатор |
OUT | request | "запрос обмена" |
Возврат из подпрограммы происходит немедленно, без ожидания окончания приема данных. Определить момент окончания приема можно с помощью подпрограмм MPI_Wait или MPI_Test с соответствующим параметром request.
Функция ожидания завершения неблокирующей операции MPI_Wait
MPI_WAIT(REQUEST, STATUS, IERROR) INTEGER REQUEST, STATUS(MPI_STATUS_SIZE), IERROR INOUT request - "запрос обмена"; OUT status - атрибуты сообщения.
Это нелокальная блокирующая операция. Возврат происходит после завершения операции, связанной с запросом request. В параметре status возвращается информация о законченной операции.
Функция проверки завершения неблокирующей операции MPI_Test
MPI_TEST(REQUEST, FLAG, STATUS, IERROR) LOGICAL FLAG INTEGER REQUEST, STATUS(MPI_STATUS_SIZE), IERROR INOUT request - "запрос обмена"; OUT flag - признак завершенности проверяемой операции; OUT status - атрибуты сообщения, если операция завершилась.
Это локальная неблокирующая операция. Если связанная с запросом request операция завершена, возвращается flag = true, а status содержит информацию о завершенной операции. Если проверяемая операция не завершена, возвращается flag = false, а значение status в этом случае не определено.
Рассмотрим пример использования неблокирующих операций и функции MPI_Wait.
CALL MPI_COMM_RANK(comm, rank, ierr) IF (rank.EQ.0) THEN CALL MPI_ISEND(a(1), 10, MPI_REAL, 1, tag, comm, request, ierr) **** Выполнение вычислений во время передачи сообщения **** CALL MPI_WAIT(request, status, ierr) ELSE CALL MPI_IRECV(a(1), 15, MPI_REAL, 0, tag, comm, request, ierr) **** Выполнение вычислений во время приема сообщения **** CALL MPI_WAIT(request, status, ierr) END IF
И, напоследок, опишем структуру переменной status.
Назначение полей переменной status:
Поля status | Обращение к полю из FORTRANа |
---|---|
Процесс-отправитель | status(MPI_SOURCE) |
Идентификатор сообщения | status(MPI_TAG) |
Код ошибки | status(MPI_ERROR) |