Чтобы завершить неблокирующий обмен, используются функции MPI_WAIT и MPI_TEST. Завершение операции посылки указывает, что отправитель теперь может изменять содержимое ячеек буфера посылки (операция посылки сама не меняет содержание буфера). Операция завершения не извещает, что сообщение было получено, но дает сведения, что оно было буферизовано коммуникационной подсистемой. Однако, если был использован синхронный режим, завершение операции посылки указывает, что соответствующий прием был инициирован и что это сообщение будет в конечном итоге принято этим соответствующим получателем.
Завершение операции приема указывает, что приемный буфер содержит принятое сообщение, что процесс-получатель теперь может обращаться к нему и что статусный объект установлен. Это не означает, что операция посылки завершена (но указывает, конечно, что посылка была инициирована).
Далее будет использоваться следующая терминология: нулевой дескриптор
есть дескриптор со значением MPI_REQUEST_NULL. Персистентный (persistent) запрос и дескриптор на него неактивны,
если запрос не связан с любым продолжающимся обменом (см. раздел 3.9).
Дескриптор является активным, если он не является нулевым или неактивным.
Состояние пусто (empty) - это состояние, которое установлено, чтобы
возвратить tag = MPI_ANY_TAG, source = MPI_ANY_SOURCE,
error = MPI_SUCCESS, и внутренне
сконфигурировано так, чтобы вызовы MPI_GET_COUNT и
MPI_GET_ELEMENTS возвращали count = 0 и MPI_TEST_CANCELLED возвращал false. Переменная состояния
устанавливается на empty, когда возвращаемое ею значение
несущественно. Состояние устанавливается таким образом, чтобы предупредить
ошибки из-за устаревшей информации.
Синтаксис функции MPI_WAIT представлен ниже.
MPI_WAIT(request, status)
INOUT | request | запрос (дескриптор) | |
OUT | status | объект состояния (статус) |
int MPI_Wait(MPI_Request *request, MPI_Status *status)
MPI_WAIT(REQUEST, STATUS, IERROR)
INTEGER REQUEST, STATUS(MPI_STATUS_SIZE), IERROR
void MPI::Request::Wait(MPI::Status& status)
void MPI::Request::Wait()
Обращение к MPI_WAIT заканчивается, когда завершена операция, указанная в запросе. Если коммуникационный объект, связанный с этим запросом, был создан вызовом неблокирующей посылки или приема, тогда этот объект удаляется при обращении к MPI_WAIT и дескриптор запроса устанавливается в MPI_REQUEST_NULL. MPI_WAIT является нелокальной операцией.
Вызов возвращает в status информацию о завершенной операции. Содержание статусного объекта для приемной операции может быть получено способом, описанным в разделе 3.2.5. Статусный объект для операции посылки может быть поставлен в очередь обращением к MPI_TEST_CANCELLED (см. раздел 3.8).
Разрешается вызывать MPI_WAIT с нулевым или неактивным аргументом запроса. В этом случае операция заканчивается немедленно со статусом empty.
Совет пользователям: Успешное завершение вызова MPI_WAIT после MPI_IBSEND подразумевает, что пользовательский буфер посылки может использоваться повторно, то есть что данные уже извлечены или скопированы в буфер, подключенный по MPI_BUFFER_ATTACH. Заметим, что с этого момента мы не можем дольше отменять посылку (см. раздел 3.8). Если соответствующий прием никогда не инициируется, тогда буфер не может быть освобожден. При этом необходимо запустить нечто вроде счетчика для вызова MPI_CANCEL (всегда способен освободить программное пространство, переданное коммуникационной подсистеме).[]
Совет разработчикам: В многопоточной среде обращение к MPI_WAIT должно блокировать только вызываемый поток, позволяя планировщику потоков планировать для исполнения другой поток.[]
Синтаксис функции MPI_TEST представлен ниже.
MPI_TEST(request, flag, status)
INOUT | request | коммуникационный запрос (дескриптор) | |
OUT | flag | true, если операция завершена (логический тип) | |
OUT | status | статусный объект (статус) |
int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status)
MPI_TEST(REQUEST, FLAG, STATUS, IERROR)
LOGICAL FLAG
INTEGER REQUEST, STATUS(MPI_STATUS_SIZE), IERROR
bool MPI::Request::Test(MPI::Status& status)
bool MPI::Request::Test()
Обращение к MPI_TEST возвращает flag = true, если операция, указанная в запросе, завершена. В этом случае статусный объект содержит информацию о завершенной операции; если коммуникационный объект был создан неблокирующей посылкой или приемом, то он входит в состояние дедлока и обработка запроса устанавливается в MPI_REQUEST_NULL. Другими словами, вызов возвращает flag = false. В этом случае значение статуса не определено. MPI_TEST является локальной операцией.
Возвращенный статус для операции приема несет информацию, которая может быть получена способом, описанным в разделе 3.2.5. Статусный объект для операции посылки несет информацию, которая может быть получена обращением к MPI_TEST_CANCELLED (см. раздел 3.8).
Можно вызывать MPI_TEST с нулевым или неактивным аргументом запроса. В таком случае операция возвращает flag = true и empty для status.
Функции MPI_WAIT и MPI_TEST могут быть использованы как для завершения, так и для приема.
Совет пользователям: Использование неблокирующего вызова MPI_TEST позволяет пользователю планировать альтернативную активность внутри одиночного потока исполнения. Запускаемый по событиям планировщик потоков может быть эмулирован периодическими обращениями к MPI_TEST.[]
Объяснение: Функция MPI_TEST возвращает flag = true точно в момент, когда функция MPI_WAIT возвращает управление; в этом случае обе функции возвращают то же самое значение статуса. Следовательно, блокируемый Wait может быть легко замещен неблокируемым Test.[]
Пример 3.11 Простое использование неблокируемой операции и 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)
**** do some computation to mask latency ****
CALL MPI_WAIT(request, status, ierr)
ELSE
CALL MPI_IRECV(a(1), 15, MPI_REAL, 0, tag, comm, request, ierr)
**** do some computation to mask latency ****
CALL MPI_WAIT(request, status, ierr)
END IF
Объект запроса может быть удален без ожидания завершения соответствующего обмена путем использования следующей операции MPI_REQUEST_FREE.
Синтаксис функции MPI_REQUEST_FREE представлен ниже.
MPI_REQUEST_FREE(request)
INOUT | request | коммуникационный запросн (дескриптор) |
int MPI_Request_free(MPI_Request *request)
MPI_REQUEST_FREE(REQUEST, IERROR)
INTEGER REQUEST, IERROR
void MPI::Request::Free()
Эта функция маркирует объект запроса для удаления и устанавливает запрос в состояние MPI_REQUEST_NULL. Продолжающейся коммуникации, которая связана с этим запросом, будет разрешено завершение. Запрос будет удален только после ее завершения.
Объяснение: Механизм MPI_REQUEST_FREE введен для повышения характеристик обмена и обеспечения удобства на стороне отправителя.[]
Совет пользователям: Как только запрос удаляется вызовом MPI_REQUEST_FREE, уже невозможно проверить успешное завершение соответствующего обмена обращением к MPI_WAIT или MPI_TEST. Если ошибка возникает позже в течение вызова, код ошибки не может быть возвращен пользователю - такая ошибка должна быть обработана как фатальная. Возникает вопрос, как узнать, когда операция завершена, когда используется MPI_REQUEST_FREE. В зависимости от логики программы могут быть другие пути, в которых программа знает, что определенные операции уже завершены и это делает использование MPI_REQUEST_FREE практичным. Например, активный запрос посылки можно было бы удалить, если логика программы такова, что приемник посылает ответ посланному сообщению - прибытие ответа информирует отправителя, что передача завершена и буфер отправителя может быть использован повторно. Активный запрос приема никогда не должен быть удален, так как процесс-получатель не будет иметь способа проверить, что прием завершен и приемный буфер свободен для нового использования.[]
Пример 3.12 Пример использования MPI_REQUEST_FREE.
CALL MPI_COMM_RANK(MPI_COMM_WORLD, rank)
IF(rank.EQ.0) THEN
DO i=1, n
CALL MPI_ISEND(outval, 1, MPI_REAL, 1, 0, req, ierr)
CALL MPI_REQUEST_FREE(req, ierr)
CALL MPI_IRECV(inval, 1, MPI_REAL, 1, 0, req, ierr)
CALL MPI_WAIT(req, status, ierr)
END DO
ELSE ! rank.EQ.1
CALL MPI_IRECV(inval, 1, MPI_REAL, 0, 0, req, ierr)
CALL MPI_WAIT(req, status)
DO I=1, n-1
CALL MPI_ISEND(outval, 1, MPI_REAL, 0, 0, req, ierr)
CALL MPI_REQUEST_FREE(req, ierr)
CALL MPI_IRECV(inval, 1, MPI_REAL, 0, 0, req, ierr)
CALL MPI_WAIT(req, status, ierr)
END DO
CALL MPI_ISEND(outval, 1, MPI_REAL, 0, 0, req, ierr)
CALL MPI_WAIT(req, status)
END IF