Программирование для многопроцессорных систем в стандарте MPI - Шпаковский Г.И., Серикова Н.В.. Программирование для многопроцессорных систем в стандарте MPI -. Организация вычислений в многопроцессорных системах
Скачать 1.61 Mb.
|
Глава 3. ПАРНЫЕ МЕЖПРОЦЕССНЫЕ ОБМЕНЫ 3.1. ВВЕДЕНИЕ Главы 3 − 6 написаны в соответствии со стандартом MPI-1.2 [20, 11], также использовались книги [4, 5, 6,7] и другие документы. Передача и прием сообщений процессами – это базовый коммуни- кационный механизм MPI. Основными операциями парного обмена являются операции send (послать) и receive (получить). Их исполь- зование иллюстрируется следующим примером: #include "mpi.h" main( argc, argv ) 7 int argc; char **argv; { char message[20]; int myrank; MPI_Status status; MPI_Init( &argc, &argv ); MPI_Comm_rank( MPI_COMM_WORLD, &myrank ); if (myrank == 0) /* код для процесса 0 */ { strcpy(message,"Hello, there"); MPI_Send(message,strlen(message),MPI_CHAR,1,99, MPI_COMM_WORLD); } else /* код для процесса 1 */ { MPI_Recv(message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD, &status); printf("received :%s:\n", message); } MPI_Finalize(); } В этом примере процесс с номером 0 ( myrank = 0 ) посылает со- общение процессу с номером 1, используя операцию посылки MPI_Send . Эта операция описывает буфер посылающего процесса, из которого извлекаются посылаемые данные. В приведенном примере посылающий буфер состоит из накопителя в памяти процесса 0, со- держащего переменную message Размещение, размер и тип буфера 46 посылающего процесса описываются первыми тремя параметрами операции send. Посланное сообщение будет содержать 13 символов этой переменной. Операция посылки также связывает с сообщением его атрибуты. Атрибуты определяют номер процесса-получателя со- общения и содержат различную информацию, которая может быть использована операцией receive , чтобы выбрать определенное сооб- щение среди других. Последние три параметра операции посылки описывают атрибуты посланного сообщения. Процесс 1 ( myrank = 1 ) получает это сообщение, используя операцию приема MPI_Recv, и данные сообщения записываются в буфер процесса-получателя. В приведенном примере буфер получателя состоит из накопителя в па- мяти процесса один, содержащего строку message . Первые три пара- метра операции приема описывают размещение, размер и тип буфера приема. Следующие три параметра необходимы для выбора входного сообщения. Последний параметр необходим для возврата информации о только что полученном сообщении. 3.2. ОПЕРАЦИИ БЛОКИРУЮЩЕЙ ПЕРЕДАЧИ И БЛОКИРУЮЩЕГО ПРИЕМА 3.2.1. Блокирующая передача MPI_SEND(buf, count, datatype, dest, tag, comm) IN buf начальный адрес буфера посылки сообщения (альтернатива) IN count число элементов в буфере посылки (неотрицательное целое) IN datatype тип данных каждого элемента в буфере передачи (дескриптор) IN dest номер процесса-получателя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) int MPI_Send (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) MPI_SEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERROR) INTEGER COUNT, DATATYPE, DEST, TAG, COMM, IERROR void MPI::Comm::Send (const void* buf, int count, const MPI::Datatype& datatype, int dest, int tag) const Семантика этого блокирующего вызова описана в 3.4. 47 3.2.2. Данные в сообщении Буфер посылки описывается операцией MPI_SEND , в которой указано количество последовательных элементов, тип которых указан в поле datatype, начиная с элемента по адресу buf. Длина сообщения задается числом элементов, а не числом байт. Число данных count в сообщении может быть равно нулю, это оз- начает, что область данных в сообщении пуста. Базисные типы дан- ных в сообщении соответствуют базисным типам данных используе- мого языка программирования. Список возможного соответствия этих типов данных для языка Fortran и MPI представлен ниже. MPI datatype Fortran datatype MPI_INTEGER INTEGER MPI_REAL REAL MPI_DOUBLE_PRECISION DOUBLE PRECISION MPI_COMPLEX COMPLEX MPI_LOGICAL LOGICAL MPI_CHARACTER CHARACTER(1) MPI_BYTE MPI_PACKED Список соответствия типов данных для языка С и MPI дан ниже. MPI datatype C datatype MPI_CHAR signed char MPI_SHORT signed short int MPI_INT signed int MPI_LONG signed long int MPI_UNSIGNED_CHAR unsigned char MPI_UNSIGNED_SHORT unsigned short int MPI_UNSIGNED unsigned int MPI_UNSIGNED_LONG unsigned long int MPI_FLOAT float MPI_DOUBLE double MPI_LONG_DOUBLE long double MPI_BYTE MPI_PACKED Типы MPI_BYTE и MPI_PACKED не имеют соответствия в язы- ках С или Fortran. Значением типа MPI_BYTE является байт. Байт не интерпретируется и отличен от символа. Различные машины могут иметь различное представление для символов или могут использовать 48 для представления символов более одного байта. С другой стороны, байт имеет то же самое двоичное значение на всех машинах. 3.2.3. Атрибуты сообщения В дополнение к описанию данных сообщение несет информацию, которая используется, чтобы различать и выбирать сообщения. Эта информация состоит из фиксированного количества полей, которые в совокупности называются атрибутами сообщения. Эти поля таковы: source, destination, tag, communicator (номер процесса-отправителя сообщения, номер процесса-получателя, тэг, коммуникатор). Целочисленный аргумент тэг используется, чтобы различать типы сообщений. Диапазон значений тэга находится в пределах 0,…,UB, где верхнее значение UB зависит от реализации. MPI требует, чтобы UB было не менее 32767. Аргумент comm описывает коммуникатор, который используется в операции обмена. Коммуникатор описывает коммуникационный контекст коммуникационной операции. Сообщение всегда принима- ется внутри контекста, в котором оно было послано; сообщения, по- сланные в различных контекстах, не взаимодействуют. Коммуникатор также описывает ряд процессов, которые разделя- ют этот коммуникационный контекст. Эта группа процессов упоря- дочена, и процессы определяются их номером внутри этой группы: диапазон значений для dest есть 0,...,n-1, где n есть число процессов в группе В MPI предопределен коммуникатор MPI_COMM_WORLD . Он разрешает обмен для всех процессов, которые доступны после ини- циализации MPI, и процессы идентифицируются их номерами в груп- пе MPI_COMM_WORLD 3.2.4. Блокирующий прием MPI_RECV (buf, count, datatype, source, tag, comm, status) OUT buf начальный адрес буфера процесса-получателя (альтернатива) IN count число элементов в принимаемом сообщении (целое) IN datatype тип данных каждого элемента сообщения (дескриптор) IN source номер процесса-отправителя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) OUT status параметры принятого сообщения (статус) int MPI_Recv (void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status) 49 MPI_RECV(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, STATUS, IERROR) INTEGER COUNT, DATATYPE, SOURCE, TAG, COMM, STATUS(MPI_STATUS_SIZE), IERROR void MPI::Comm::Recv (void* buf, int count, const MPI::Datatype& datatype, int source, int tag) const Буфер получения состоит из накопителя, содержащего последова- тельность элементов, размещенных по адресу buf Тип элементов указан в поле datatype . Длина получаемого сооб- щения должна быть равна или меньше длины буфера получения, в противном случае будет иметь место ошибка переполнения. Если со- общение меньше размера буфера получения, то в нем модифицируют- ся только ячейки, соответствующие длине сообщения. Прием сообщения осуществляется, если его атрибуты соответст- вуют значениям источника, тэга и коммуникатора, которые указаны в операции приема. Процесс-получатель может задавать значение MPI_ANY_SOURCE для отправителя и/или значение MPI_ANY_TAG для тэга, определяя, что любой отправитель и/или тэг разрешен. Нельзя задать произвольное значение для comm . Следо- вательно, сообщение может быть принято, если оно адресовано дан- ному получателю и имеет соответствующий коммуникатор. Тэг сообщения задается аргументом tag операции приема. Аргу- мент отправителя, если он отличен от MPI_ANY_SOURCE , задается как номер внутри группы процессов, связанной с тем же самым ком- муникатором. Следовательно, диапазон значений для аргумента от- правителя есть {0,...,n-1} U {MPI_ANY_SOURCE} , где n есть количе- ство процессов в этой группе. Отметим ассиметрию между операциями посылки и приема. Опе- рация приема допускает получение сообщения от произвольного от- правителя, в то время как в операции посылки должен быть указан уникальный получатель. Допускается ситуация, когда имена источника и получателя сов- падают, то есть процесс может посылать сообщение самому себе (это небезопасно, поскольку может привести к дедлоку ( deadlock )). 50 3.2.5. Возвращаемая статусная информация Источник или тэг принимаемого сообщения могут быть неизвест- ны, если в операции приема были использованы значения типа ANY Иногда может потребоваться возвратить различные коды ошибок для каждого запроса. Эта информация возвращается с помощью аргу- мента status операции MPI_RECV. Тип аргумента status определяется MPI. Статусные переменные размещаются пользователем явно, то есть они не являются системны- ми объектами. В языке С status есть структура, которая содержит три поля, назы- ваемые MPI_SOURCE, MPI_TAG и MPI_ERROR . Следовательно, status.MPI_SOURCE , status.MPI_TAG и status.MPI_ERROR со- держат источник, тэг и код ошибки принятого сообщения. В языке Fortran status есть массив целых значений размера MPI_STATUS_SIZE . Константы MPI_SOURCE, MPI_TAG и MPI_ERROR определяют объекты, которые хранят поля источника, тэга и ошибки. Следовательно, status(MPI_SOURCE), status(MPI_TAG) и status(MPI_ERROR) содержат соответственно источник, тэг и код ошибки принимаемого сообщения. Вызовы передачи сообщений не модифицируют значения полей кода ошибки статусных переменных. Статусный аргумент также воз- вращает информацию о длине принятого сообщения. Эта информация не является доступной непосредственно, как поле статусной перемен- ной, и требуется вызов MPI_GET_COUNT , чтобы «декодировать» эту информацию. MPI_GET_COUNT(status, datatype, count) IN status статус операции приема (статус ) IN datatype тип данных каждого элемента приемного буфера (дескриптор) OUT count количество полученных единиц (целое) int MPI_Get_count (MPI_Status *status,MPI_Datatype datatype, int *count) MPI_GET_COUNT(STATUS, DATATYPE, COUNT, IERROR) INTEGER STATUS(MPI_STATUS_SIZE), DATATYPE, COUNT, IERROR int Status :: Get_count (const MPI :: Datatype& datatype) const Операция MPI_GET_COUNT возвращает число полученных эле- ментов. Аргумент datatype следует сопоставлять с аргументом из операции приема, которая устанавливает статусную переменную. 51 3.3. СООТВЕТСТВИЕ ТИПОВ ДАННЫХ И ПРЕОБРАЗОВАНИЕ ДАННЫХ 3.3.1. Правила соответствия типов данных Передача данных содержит следующие три фазы: 1) данные выталкиваются из буфера процесса-отправителя, и части сообщения объединяются; 2) сообщение передается от отправителя к получателю; 3) данные выделяются из получаемого сообщения и помещаются в буфер получателя. Соответствие типов должно отслеживаться на каждой из трех фаз: • тип каждой переменной в буфере посылки должен соответствовать типу, указанному для этого элемента в операции посылки; • тип, описанный в операции посылки, должен соответствовать ти- пу, указанному в операции приема; • тип каждой переменной в приемном буфере должен соответство- вать типу, указанному для нее в операции приема. Программа, в которой не соблюдаются эти три правила, является неверной. Типы отправителя и получателя (фаза два) соответствуют друг другу, если обе операции используют одинаковые названия. Это озна- чает, что MPI_INTEGER соответствует MPI_INTEGER, MPI_REAL соответствует MPI_REAL , и так далее. Тип переменной в хост–программе (главной программе) соответ- ствует типу, указанному в операции обмена, если название типа дан- ных, используемое этой операцией, соответствует базисному типу пе- ременной хост–программы. Например, элемент с названием типа MPI_INTEGER соответствует в языке Fortran переменной типа INTEGER . Таблица, описывающая соответствие для языков Fortran и C, представлена в параграфе 3.2.2. Имеется два исключения из этого последнего правила: элемент с названием типа MPI_BYTE или MPI_PACKED может соответство- вать любому байту памяти (на байт-адресуемой машине), без учета типа переменной, которая содержит этот байт. Тип MPI_PACKED используется для передачи данных, которые были явно упакованы, или для получения данных, которые будут явно распакованы (3.10). Тип MPI_BYTE позволяет передавать двоичное значение байта из памяти. 52 Правила соответствия типов можно разделить на три категории: • Коммуникация типизированных значений: типы данных соответ- ствующих элементов в программе передачи, в вызове операции передачи, в вызове операции приема и в программе приема долж- ны соответствовать друг другу. • Коммуникация нетипизированных значений: нет никаких требова- ний по типам соответствующих элементов в передающей и при- нимающей программах. • Коммуникация, применяющая упакованные данные, где исполь- зуется MPI_PACKED Пример 3.1. Отправитель и получатель указывают типы соответствия. CALL MPI_COMM_RANK(comm, rank, ierr) IF(rank.EQ.0) THEN CALL MPI_SEND(a(1), 10, MPI_REAL, 1, tag, comm, ierr) ELSE CALL MPI_RECV(b(1), 15, MPI_REAL, 0, tag, comm, status, ierr) END IF Код корректен, если a, b – действительные массивы размера ≥ 10. Пример 3.2. Отправитель и получатель указывают разные типы. CALL MPI_COMM_RANK(comm, rank, ierr) IF(rank.EQ.0) THEN CALL MPI_SEND(a(1), 10, MPI_REAL, 1, tag, comm, ierr) ELSE CALL MPI_RECV(b(1), 40, MPI_BYTE, 0, tag, comm, status, ierr) END IF Код ошибочен: отправитель и получатель описывают различные типы данных. Пример 3.3. Отправитель и получатель описывают передачу нетипи- зированных значений. CALL MPI_COMM_RANK(comm, rank, ierr) IF(rank.EQ.0) THEN CALL MPI_SEND(a(1), 40, MPI_BYTE, 1, tag, comm, ierr) ELSE CALL MPI_RECV(b(1), 60, MPI_BYTE, 0, tag, comm, status, ierr) END IF 53 Код правилен безотносительно к типу и размеру а и b (кроме слу- чая, когда эти результаты выходят за границы памяти). 3.3.2. Преобразование данных Одной из целей MPI является поддержка параллельных вычисле- ний в неоднородной среде. Связь в такой среде может потребовать следующего преобразования данных: • преобразования типа – изменяется тип данных значения, например округлением REAL в INTEGER; • преобразования представления – изменяется двоичное представле- ние значения, например, от Hex floating point к IEEE floating point. Правила соответствия типов приводят к тому, что обмен в MPI ни- когда не влечет за собой преобразования типов. С другой стороны MPI требует, чтобы преобразование представления выполнялось, ко- гда типизированное значение передается через среды, которые ис- пользуют различные представления для типов данных этих значений. MPI не описывает правила для преобразования представления. Пред- полагается, что такое преобразование должно сохранять целые, логи- ческие или знаковые значения и преобразовывать значения с плаваю- щей точкой к ближайшему значению, которое может быть представ- лено на целевой системе. Во время преобразования с плавающей точкой могут иметь место исключения по переполнению и потере значимости. Преобразование целых также может приводить к исключениям, когда значения, кото- рые могут быть представлены в одной системе, не могут быть пред- ставлены в другой системе. Исключения при преобразовании пред- ставления приводят к невозможности обмена. Ошибка имеет место либо на операции посылки, либо на операции приема, либо на обеих операциях. Если значение, посылаемое в сообщении, не типизировано (на- пример, типа MPI_BYTE ), тогда двоичное представление байта, хра- нимое на стороне получателя, идентично двоичному представлению байта, загруженного на стороне отправителя. Это сохраняется вне за- висимости от того, работают ли отправитель и получатель в одной и той же или различающихся средах. Никакого преобразования не нужно, когда программа MPI работа- ет в однородной системе – все процессы выполняются в той же самой среде. 54 Рассмотрим примеры 3.1 – 3.3. Первая программа правильна, если a и b являются действительными массивами размера ≥ 10. Если отпра- витель и получатель работают в различных средах, тогда десять дей- ствительных значений, которые извлекаются из буфера отправителя, будут преобразованы в представление для действительных чисел на приемной стороне прежде, чем они будут записаны в приемный бу- фер. В то время, как число действительных чисел, извлекаемых из бу- фера отправителя, равно числу действительных чисел, хранимых в приемном буфере, то число хранимых байтов не обязано быть равным числу загруженных байтов. Например, отправитель может использо- вать четырехбайтовое представление для действительных чисел, а по- лучатель – восьмибайтовое. Вторая программа содержит ошибки, и ее поведение является не- определенным. Третья программа правильная. Точно та же последовательность из сорока байтов, которая была загружена из буфера посылки, будет за- писана в приемный буфер, даже если получатель и отправитель рабо- тают в различных средах. Посланное сообщение имеет точно ту же длину и точно то же двоичное представление, как и принятое сообще- ние. Если a и b принадлежат к различным типам или они имеют оди- наковый тип, но различное представление, то биты, хранимые в при- емном буфере, могут кодировать значения, которые отличаются от значений, закодированных теми же битами в буфере передачи. Преобразование представления также относится и к атрибутам со- общения: источник, приемник и тэг являются целыми числами и мо- гут нуждаться в преобразовании. MPI не поддерижвает межъязыко- вый обмен. Поведение программы не определено, если сообщение по- слано процессом в языке С, а принято процессом в языке Fortran. |