Обмен сообщениями типа «точка-точка»

Эта группа функций обеспечивает псрсдачу/присм сообщений между парами ветвей одного коммуникатора. Передача/'прием могут быть блокирующими (выполнение ветви останавливается вплоть до момента завершения обмена данными) и неблокирующими (ветвь получает управление сразу после того, как библиотека обработала аргументы функции).

int MPI_Send(void *buf int count, MPI Datatype datatype, int dest, int tag, MPI Comm comm); - передача сообщения с блокировкой. Здесь и далее три аргумента buf count и datatype полностью определяют буфер с данными. Аргументы dest и tag однозначно определяют индекс ветви-получателя сообщения в коммуникаторе сотт и порядковый номер сообщения.

int MPI_Recv(void *buf, int count, MPI Datatype datatype, int source, int tag, MPI Comm comm, MPI Status *status); - прием сообщения, отправляемого функцией MPI Send, с блокировкой. Смысл аргументов функции точно такой же, как и у функции MPI Send (аргумент source кроме очевидного номера ветви-отправителя в коммуникаторе сотт может иметь значение MPI_ANY_SOUR.CE, т. е. отправитель - любая ветвь коммуникатора), однако здесь есть дополнительный аргумент status, являющийся указателем на структуру, поля которой позволяют получить дополнительные сведения о принятом сообщении (их перечень определен исходя из нужд функций без блокировки):

  • - int cancelled - 0, если прием завершился успешно, не 0 в противном случае;
  • - int count - размер буфера для принимаемых данных (не фактическое количество принятых данных);
  • - int MPIERROR - код ошибки, если прием завершился с ошибкой (может не совпадать с тем, что возвращает функция MPIRecv);
  • - int MPI_SOURCE - идентификатор вегви-отправителя данных;
  • - int MPI TAG - тэг принятого сообщения.

Если ни одна ветвь не отправит в данную ветвь сообщения с указанным тэгом, то ветвь-получатель никогда не получит управления, т. с. параллельная программа просто зависнет.

Операции MPI_Send и MPI Recv являются основными для обменов типа «ветвь-ветвь». Однако их использование в реальных программах сопряжено с известными трудностями и в большинстве случаев приводит к невысокой производительности.

Поэтому в MPI дополнительно поддерживаются следующие разновидности операций передачи.

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

Передача по готовности. Собственно передача начинается только в том случае, если начата приемка сообщения. В этот момент без ожидания окончания приема выполняется возврат в ветвь.

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

где

  • - / (Immediate) - обозначает неблокирующую операцию;
  • - R (Ready) - передача по готовности;
  • - S (Synchronous) - синхронный;
  • - В (Buffer)- буферизированный.
  • (только первая буква после подчеркивания является прописной, все остальные - строчные, как например MPI_Ibsend).

Аргументы всех блокирующих (без первой после символа подчеркивания буквы I) функций точно такие же, как у функции MPISend. Добавочным аргументом неблокирующих функций MPI_I*send является дескриптор так называемой квитанции, который в программе должен быть объявлен так:

MPIRequest request; (имя переменной может отличаться от имени request). Квитанции используются библиотекой MPI для синхронизации работы ветви и процессов доставки сообщения.

Кроме блокирующей функции приема сообщения MPI Recv существует ее неблокирующий вариант MPl_lrecv с добавочным аргументом-квитанцией.

Запущенную ранее неблокирующую операцию приема/передачи в любой момент до ее завершения можно отменить, вызвав функцию

int MPI Cancel (МPI Request *request);

Статус принимаемого сообщения можно получить, не читая из MPI самого сообщения. Это делается либо с помощью блокирующей функции:

int MPI_Probe(int source, int tag, MPI_Comm comm, MPIS tat us *status);

либо с помощью неблокирующей функции:

int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *Jlag, MPIStatus ^status);

Проверить, завершилась ли одиночная неблокирующая операция, можно путем вызова функции

int MPI Test (MPI Request *request, int flag, MPIStatus *status); - эта функция проверяет состояние операции, связанной с квитанцией request и формирует значение флага false, если операция еще не завершилась, и значение true, если операция завершилась (в этот момент становятся актуальными поля структуры status).

Дождаться завершения одной операции можно, вызвав функцию

int MPI Wait (MPI Request *request, MPI Status *status); - вместо адреса структуры status может быть указано значение MPI_STATUS_ IGNORE. Тогда определить, нормально ли завершилась операция, можно только по коду завершения, возвращаемому функцией MPI Test или MPI Wait. Эта функция возвращает управление в ветвь только тогда, когда завершается неблокирующая операция MPI, в вызове которой был указан данный request. В этот момент формируются / модифицируются поля структуры status (если она указана).

Если было запущено более одной неблокирующей операции присма/передачи, то с помощью функции:

int MPI_Testany(int count, MPIRequest array of requests[], int *index, int flag, MPIStatus *status); можно узнать, не завершилась ли хотя бы одна из них (любая, ее порядковый номер будет записан в аргумент index, если аргумент flag будет после вызова иметь значение true).

Проверить завершение произвольного количества (но не обязательно всех) операций присма/псредачи, связанных с квитанциями из массива array_of_requests, можно с помощью функции

int MPI Testsomefint incount, MPI Request array of requests[], int *outcount, int array_of_indices[], MPI_Status array_of_statuses[]); Аргумент incount (как и count в предыдущей функции) определяет размер массивов array_of requests, array of' indices и array ofjstatuses. Аргумент outcount предназначен для возврата количества завершившихся операций, индексы соотвествующих квитанций будут записаны в массив array of indices.

Проверить факт завершения всех операций приема/передачи, квитанции которых содержатся во втором аргументе, можно с помощью функции

int MPI_Testall(int count, MPI Request array of requestsf], int flag, MPI Status array_of_statuses[J);

Проверить, отменена или нет операция присма/передачи, можно с помощью функции, устанавливающей flag:

int MPI_Test_cancelled(MPI Status *status, int flag);

Ожидать завершения любой ранее запущенной операции передачи/приема, квитанции на которые указаны в массиве, передаваемом в качестве второго аргумента, можно с помощью функции

int MPI Waitanyfint count, MPI Request array_oj'requests[], int *index, MPI Status array_of_statuses[J); - индекс завершившейся операции заносится в аргумент index.

Ожидать завершения всех ранее запущенных операций передач/ приемов, квитанции на которые указаны в массиве, передаваемом в качестве второго аргумента, можно с помощью функции:

int MPI_ Wa it all (int count, MPI Request array of'requests [],

MPI Status array of'statuses[J);

Ожидать завершения любого количества ранее запущенных операций передачи/приема, квитанции на которые указаны в массиве, передаваемом в качестве второго аргумента, можно с помощью функции

int MPI_Waitsome(int incount, МPI Request array_of_requests[], int *outcount, int array of ' indices[], MPlStatus array of statuses[]);

Все функции MPIWaiC и MPI_Test* автоматически освобождают квитанции завершившихся неблокирующих операций приема/передачи и устанавливают соответствующие дескрипторы в состояние MPIREQUESTNULL. Использовать такую квитанцию впоследствии нельзя, се надо заново инициализировать.

Есть функция, с помощью которой можно узнать, завершилась ли операция приема/передачи без деструкции соответствующей квитанции:

int MPIR eq uest_get_sta tus (MPIR eq uest request, int *Jlag, MPIStatus *status);

Квитанция, связанная с любой неблокирующей операцией приема/передачи, может быть явно «освобождена» без ожидания завершения этой операции:

int MPI_Request_free(MPl Request *request) ;

Параметр request устанавливается в значение MPI_REQUEST_ NULL. Связанная с квитанцией операция не прерывается, однако проверить ее завершение с помощью MPIWait или MPITest уже нельзя.

Любая блокирующая операция приема/передачи может быть инициирована в так называемом «отложенном» режиме. Это означает, что библиотека получает и запоминает аргументы этой операции, по начинает выполнять ее только при вызове одной из функций MPIStart или MPIStartall. Функции инициирования выглядят так:

int MPISendinit (void *buf, int count, MPI Datatype datatype, int dest, int tag, MPI_Comm comm, MPIJRequest *request);

int MPIRsendinit (void *buf, int count, MPl Datatype datatype, int dest, int tag, MPIjComm comm, MPl Request *request);

int MPI Ssend init (void *buf, int count, MPl Datatype datatype, int dest, int tag, MPl Comm comm, MPl Request *request);

int MPl В send_init (void *huf, int count, MPI_Datatype datatype, int dest, int tag, MPl Comm comm, MPl Request *request);

int MPI_Recv_init (void *buf, int count, MPI_Datatype datatype, int source, int tag, MPl Comm comm, MPl Request *request);

Во всех функциях *send_init добавлен (по сравнению с соответствующей функцией *send) последний аргумент MPl Request *request.

В функции MPI RecvJnit этот аргумент появился взамен аргумента MPIStatus *stat us.

Отложенные операции приема/передачи можно запустить на выполнение с помощью функций

int MPIStart (MPIRequest * request);

int MPI_Startall(int count, MPI Request array_of requests[]);

В MPI есть группа функций, совмещающих операции приема и передачи. Они достаточно часто применяются при программировании «каскадных» или «линейных» схем, когда необходимо осуществлять однотипный обмен данными между ветвями. Примером является функция:

int MPISendrecv (void* sendbuffer, int sendcount, MPI Datatype senddatatype, int dest, int sendtag, void* recvbuffer, int recvcount, MPI Datatype recvdatatype, int src, int recvtag, MPI Comm comm, MPI Status* status); - эта функция копирует данные из массива sendbuffer ветви с идентификатором src в буфер recvbuffer ветви с идентификатором dest.

Другая полезная функция:

int МPI Sendrecv replace (void* buffer, int count, MPI Datatype datatype, int dest, int sendtag, int src, int recvtag,MPI_Comm comm, MPI Status* status); - данные также передаются из ветви src в ветвь dest, но используется только один буфер (вначале сообщение из него передается, потом в него же принимается).

 
Посмотреть оригинал
< Пред   СОДЕРЖАНИЕ   ОРИГИНАЛ     След >