Н.В. Вдовикина, И.В. Машечкин, А.Н. Терехин, А.Н. Томилин - Операционные системы - взаимодействие процессов (2008) (1114653), страница 31
Текст из файла (страница 31)
В некоторыхслучаях знание этого факта позволяет MPI использовать болеекороткий протокол для установления соединения и тем самымповыситьпроизводительностьпередачиданных.Однако,использование этой функции ограничено теми ситуациями, когдаветви-отправителю известно о том, что ветвь-адресат уже ожидает192сообщение. В случае, если на самом деле ветвь-адресат неинициировала прием сообщения, MPI_Rsend() завершается сошибкой.Прием сообщений в блокирующем режимеПрием сообщений в блокирующем режиме осуществляетсяфункцией MPI_Recv():#include <mpi.h>int MPI_Recv(void* buf, int count, MPI_Datatypedatatype, int source, int tag, MPI_Comm comm,MPI_Status *status);Она имеет следующие аргументы:buf – указатель на буфер, в котором нужно разместитьтело принимаемого сообщения;count – максимальное количество элементов заданноготипа, которое может уместиться в буфере;datatype–типэлементоввтелеполучаемогосообщения;– уникальный номер ветви-отправителя.
Спомощью этого параметра процесс получатель может указатьконкретного отправителя, от которого он желает получитьсообщение, либо сообщить о том, что он хочет получитьсообщение от любой ветви данного коммуникатора – в этомслучае в качестве значения параметра указывается константаdestMPI_ANY_SOURCE;tag – тэг (бирка) сообщения. Ветвь-получатель можетинициировать прием сообщения с конкретным значением тэга,либо указать в этом параметре константу MPI_ANY_TAG дляполучения сообщения с любым значением тэга;–коммуникационныйсообщение;commкоммуникатор,описывающийконтекст, в котором принимаетсяstatus – указатель на структуру типа MPI_Status, полякоторой будут заполнены функцией MPI_Recv().
Используяэтуструктуру,ветвь-получательможетузнатьдополнительную информацию о сообщении, такую как,например, его фактический размер, а также фактическиезначения тэга и отправителя в том случае, еслииспользовались константы MPI_ANY_SOURCE и MPI_ANY_TAG.193Если в момент вызова MPI_Recv() нет ни одного сообщения,удовлетворяющего заданным критериям (т.е.
посланного череззаданный коммуникатор и имеющего нужного отправителя и тэг, вслучае, если были заданы их конкретные значения), то выполнениеветви блокируется до момента поступления такого сообщения. Влюбом случае, управление возвращается процессу лишь тогда, когдасообщение будет получено, и его данные будут записаны в буферbuf и структуру status.Отметим, что фактический размер принятого сообщенияможет быть меньше, чем указанная в параметре count максимальнаяемкость буфера. В этом случае сообщение будет успешно получено,его тело записано в буфер, а остаток буфера останется нетронутым.Для того, чтобы узнать фактический размер принятого сообщения,служит функция MPI_Get_count():#include <mpi.h>int MPI_Get_count(MPI_Statusdatatype, int *count);*status,MPI_DatatypeЭтой функции передаются в качестве параметров указатель наструктуру типа MPI_Status, которая была заполнена в моментвызова функции приема сообщения, а также тип данных,образующих тело принятого сообщения.
Параметр countпредставляет собой указатель на переменную целого типа, вкоторую будет записано количество элементов типа datatype,образующих тело принятого сообщения.Если же при приеме сообщения его размер окажется большеуказанной максимальной емкости буфера, функция MPI_Recv()вернет соответствующую ошибку.
Для того чтобы избежать такойситуации, можно сначала вызвать функцию MPI_Probe(), котораяпозволяет получить информацию о сообщении, не принимая его:#include <mpi.h>int MPI_Probe(int source, int tag, MPI_Comm comm,MPI_Status *status);Параметры этой функции аналогичны последним 4-мпараметрам функции MPI_Recv(). Функция MPI_Probe(), как иMPI_Recv(), возвращает управление только при появлениисообщения, удовлетворяющего переданным параметрам, и заполняетинформацией о сообщении структуру типа MPI_Status, указатель накоторую передается в последнем параметре.
Однако, в отличие отMPI_Recv(), вызов MPI_Probe() не осуществляет собственно прием194сообщения. После возврата из MPI_Probe() сообщение остается вовходящей очереди и может быть впоследствии получено одним извызовов приема данных, в частности, MPI_Recv().Пример 34. MPI: приемнеизвестен заранее.сообщения,размеркоторогоВ данном примере приведена схема работы с сообщением,размер которого заранее неизвестен. Ветвь с номером 0 посылаетветви с номером 1 сообщение, содержащее имя исполняемойпрограммы (т.е.
значение нулевого элемента в массиве аргументовкомандной строки). В ветви с номером 1 вызов MPI_Probe()позволяет получить доступ к описывающей сообщение структуретипа MPI_Status. Затем с помощью вызова MPI_Get_count() можноузнать размер буфера, который необходимо выделить для приемасообщения.#include <mpi.h>#include <stdio.h>int main(int argc, char **argv){int size, rank;MPI_Init(&argc,библиотеку */&argv);/*ИнициализируемMPI_Comm_size(MPI_COMM_WORLD, &size);/*Узнаемколичествоприложении...
*/задачвзапущенномMPI_Comm_rank (MPI_COMM_WORLD, &rank);/* ...и свой собственный номер: от 0 до (size1) */if ((size > 1) && (rank == 0)) {/*задачасообщение*/сномером0отправляетMPI_Send(argv[0],strlen(argv[0]),MPI_CHAR, 1, 1, MPI_COMM_WORLD);printf("Sentargv[0]);toprocess1:\"%s\"\n",} else if ((size > 1) && (rank == 1)) {195/* задача с номером 1 получает сообщение*/int count;MPI_Status status;char *buf;MPI_Probe(0, 1, MPI_COMM_WORLD, &status);MPI_Get_count(&status, MPI_CHAR, &count);buf=(charsizeof(char));*)malloc(countMPI_Recv(buf,count,MPI_CHAR,MPI_COMM_WORLD, &status);printf("Received\"%s\"\n", buf);from0,process*1,0:}/* Все задачи завершают выполнение */MPI_Finalize();return 0;}7.2.6 Коммуникации «точка-точка».
Неблокирующий режимВо многих приложениях для повышения производительностипрограммы бывает выгодно совместить отправку или приемсообщений с основной вычислительной работой, что удается прииспользовании неблокирующего режим приема и отправкисообщений. В этом режиме вызов функций приема либо отправкисообщения инициирует начало операции приема/передачи и сразупосле этого возвращает управление вызвавшей ветви, а сам процесспередачи данных происходит асинхронно.Однако, для того, чтобыинициировавшая операциюприема/передачи ветвь имела возможность узнать о том, чтооперациязавершена,необходимокаким-тообразомидентифицировать каждую операцию обмена.
Для этого в MPIсуществует механизм так называемых «квитанций» (requests), дляописания которых служит тип данных MPI_Request. В моментвызова функции, инициирующей операцию приема/передачи,создается квитанция, соответствующая данной операции, и черезпараметр-указатель возвращается вызвавшей ветви. Впоследствии,используя эту квитанцию, ветвь может узнать о завершении ранееначатой операции.196Отсылка и прием сообщений в неблокирующем режимеДля отсылки и приема сообщений в неблокирующем режимеслужат следующие базовые функции:#include <mpi.h>int MPI_Isend(void* buf, int count, MPI_Datatypedatatype, int dest, int tag, MPI_Comm comm,MPI_Request *request);int MPI_Irecv(void* buf, int count, MPI_Datatypedatatype, int source, int tag, MPI_Comm comm,MPI_Request *request);Все их параметры, кроме последнего, аналогичны параметрамфункций MPI_Send() и MPI_Recv().
Последним параметром служитуказатель на переменную типа MPI_Request, которая после возвратаиз данного вызова будет содержать квитанцию для инициированнойоперации отсылки или приема данных.Возврат из функции MPI_Isend() происходит сразу же, кактолько система инициирует операцию отсылки сообщения, и будетготова начать копировать данные из буфера, переданного впараметре buf. Важно понимать, что возврат из функцииMPI_Isend() не означает, что этот буфер можно использовать подругому назначению! Вызвавший процесс не должен производитьникаких операций над этим буфером до тех пор, пока не убедится втом, что операция отсылки данных завершена.Возврат из функции MPI_Irecv() происходит сразу же, кактолько система инициирует операцию приема данных и будет готованачать принимать данные в буфер, предоставленный в параметреbuf, вне зависимости от того, существует ли в этот моментсообщение, удовлетворяющее остальным переданным параметрам(source, tag, comm).
При этом вызвавший процесс не можетполагать, что в буфере buf находятся принятые данные до тех пор,пока не убедится, что операция приема завершена. Отметим также,что у функции MPI_Irecv() отсутствует параметр status, так какна момент возврата из этой функции информация о принимаемомсообщении, которая должна содержаться в status, может быть ещенеизвестна.В неблокирующем режиме, также как и в блокирующем,доступныразличные варианты буферизации при отправкесообщения. Для явного задания режима буферизации, отличного отстандартного, служат следующие функции:197#include <mpi.h>int MPI_Issend(void* buf, int count, MPI_Datatypedatatype,intdest,inttag,MPI_Commcomm,MPI_Request *request);int MPI_Ibsend(void* buf, int count, MPI_Datatypedatatype,intdest,inttag,MPI_Commcomm,MPI_Request *request);int MPI_Irsend(void* buf, int count, MPI_Datatypedatatype,intdest,inttag,MPI_Commcomm,MPI_Request *request);Отметим, что поскольку MPI_Ibsend() предполагаетобязательную буферизацию сообщения, а MPI_rsend() используетсялишь тогда, когда на принимающей стороне уже гарантированноинициирован прием сообщения, их использование лишь в некоторыхслучаях может дать сколь либо заметный выигрыш по сравнению саналогичными блокирующими вариантами, так как разницу вовремени их выполнения составляет лишь время, необходимое длякопирования данных.Работа с квитанциямиБиблиотека MPI предоставляет целое семейство функций, спомощью которых процесс, инициировавший одну или несколькоасинхронных операций приема/передачи сообщений, может узнать остатусе их выполнения.