Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции_MPI_OpenMP.doc
Скачиваний:
4
Добавлен:
16.11.2019
Размер:
1.09 Mб
Скачать
  1. Буферизующий режим передачи

Функции передачи/приема MPI_Bsend(….) и MPI_Recv(…..), параметры те же.

В этом режиме операция передачи стартует и может завершиться до выставления соответствующей операции приема, в этом случае MPI должен буферизировать выходящее сообщение, чтобы позволить закончить операцию передачи. Если буфер, в который копируется сообщение меньше размера сообщения, то произойдет ошибка. Размером буфера управляет пользователь.

Присоединение и отсоединение буфера

Максимальный размер пересылаемого сообщения в MPICH 1.2.7 равен 500 элементам типа double, т.е. 4000 байт. Однако мы можем переопределить буфер для буферизации сообщений. Т.к. буферизация производиться передающим процессом, то создавать буфер можно только на процессе-передатчике.

int MPI_Buffer_attach( void* buffer, int size) присоединяет буфер,

созданный в памяти, который будет использоваться для буферизации выходящих сообщений. Только один буфер может быть присоединен к процессу одновременно.

IN

buffer

адрес начала буфера

IN

size

размер буфера в байтах

int MPI_Buffer_detach( void* buffer, int* size) отсоединяет буфер.

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

  1. Синхронный режим передачи

Функции передачи/приема MPI_Ssend(….)и MPI_Recv(…..), параметры те же. В этом режиме операция передачи может стартовать независимо от того, выставлена соответствующая операция приема или нет. Однако передача будет завершена, только если соответствующая операция приема выставлена, и начала получать сообщение.

  1. Режим по готовности

Функции передачи/приема MPI_Rsend(….)и MPI_Recv(…..), параметры те же. В этом режиме операция передачи может стартовать, только если выставлена соответствующая операция приема. В противном случае, произойдет ошибка. Завершение операции передачи не зависит от состояния соответствующей операции приема, и означает только, что буфер передатчика может быть использован.

Возможные варианты несоответствия размеров передаваемых и принимаемых сообщений.

  1. Размер сообщения меньше равен, размеру приемного буфера – нет ошибки, в приемном буфере изменятся только элементы, соответствующие размеру пришедшего сообщения/размер типа данных в байтах (или count)

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

В качестве номера процесса-отправителя можно указать предопределенную константу MPI_ANY_SOURCE - признак того, что подходит сообщение от любого процесса. В качестве идентификатора принимаемого сообщения можно указать константу MPI_ANY_TAG - признак того, что подходит сообщение с любым идентификатором.

По практическому использованию режимов можно привести следующие рекомендации:

  1. Режим передачи по готовности формально является наиболее быстрым, но используется достаточно редко, т.к. обычно сложно гарантировать готовность операции приема,

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

  3. Синхронный режим является наиболее медленным, т.к. требует подтверждения приема. В тоже время, этот режим наиболее надежен.

Лекция 3

Интерфейс MPI. Двухточечные операции передачи данных. Не блокирующий режим передачи данных. Функции проверки и отмены сообщений

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

Очевидно, что передача сообщения даже в самой быстрой коммуникационной среде – занимает определенное время. Чтобы процессор в это время не простаивал, в MPI был реализован механизм не блокирующих обменов, который часто приводит к улучшению производительности выполнения параллельных вычислений.

M PI_Isend()

M PI_Ibsend() MPI_Irecv()

M PI_Issend()

MPI_Irsend()

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

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

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

Дескриптор запроса (request)

Все не блокирующие вызовы и блокирующая операция приема создают объект коммуникационного запроса и связывают его с дескриптором запроса (request). Дескриптор запроса является коммуникационным объектом. Когда операция передачи или приема завершена, дескриптор запроса устанавливается в MPI_REQUEST_NULL, при этом статусный объект (status), связанный с этим запросом, инициализируется (происходит занесение значений в поля структуры status).

В не блокирующих обменах дескриптор запроса (request) используется при ожидании завершения обмена и определения статуса (status) обмена.

Между запросом и статусом существует взаимно однозначное соответствие. Состояние статуса может соответствовать пришедшему сообщению или быть empty (пусто), в этом случае tag = MPI_ANY_TAG, error = MPI_SUCCESS, source = MPI_ANY_SOURCE,

Функции завершения обменов

Чтобы завершить не блокирующий обмен, используются функции MPI_Wait, MPI_Test. Операция завершения обменов MPI_Wait является блокирующей, MPI_Testне блокирующей.

Функции завершения обмена с одной стороны необходимы для правильной работы MPI (указывая, что статусный объект установлен, приемный буфер содержит принятое сообщение и процесс-получатель может обращаться к нему), с другой стороны для того, чтобы программист знал, что обмен завершен и можно использовать данные из буфера обмена (процесса отправителя или получателя) не опасаясь того, что они будут испорчены.

MPI_Test (request, flag, status)

INOUT request коммуникационный запрос (дескриптор запроса)

OUT flag true/false, если операция завершена/не завершена (логический тип)

OUT status статусный объект (статус)

Обращение к MPI_Test возвращает flag = true, если операция, определенная дескриптором запроса, завершена. В таком случае status содержит информацию о завершенной операции, дескриптор запроса request принимает значение MPI_REQUEST_NULL. Иначе, вызов возвращает flag = false и значение статуса не определено. MPI_Test является локальной операцией.

MPI_Test можно вызывать с нулевым (MPI_REQUEST_NULL) аргументом запроса. В этом случае операция заканчивается немедленно и возвращает flag = true и empty для status.

MPI_Wait (request, status)

INOUT request запрос (дескриптор)

OUT status объект состояния (статус)

MPI_Wait разблокирует процесс, когда операция передачи данных, указанная в запросе завершена, в этом случае дескриптор запроса устанавливается в MPI_REQUEST_NULL, в status возвращается информация о завершенной операции.

Разрешается вызывать MPI_Wait с нулевым (MPI_REQUEST_NULL) аргументом запроса. В этом случае операция заканчивается немедленно со статусом empty.

Множественные завершения

MPI_ Waitany или MPI_Testany используются для ожидания завершения одной из нескольких операций.

MPI_Waitall или MPI_Testall используются для завершения всех операций в списке.

Waitsome или MPI_Testsome используются для завершения некоторых операций из списка.

MPI_Waitany (count, array_of_requests, index, status)

IN count длина списка (целое)

IN/OUT array_of_requests массив запросов (массив дескрипторов)

OUT index индекс дескриптора завершенной операции (целое)

OUT status статусный объект (статус)

MPI_Waitany блокирует работу процесса до тех пор, пока не завершится любая операция, из указанного массива дескрипторов запроса. В функцию подается массив из count запросов, используя который функция завершает ждущие окончания операции приема/передачи данных. Смысл суффикса ANY заключается в том, выбирается для завершения не конкретное сообщение с однозначно определенными атрибутами в запросе, а первые в очереди, т.е. выполняется произвольный выбор. Операция возвращает в index порядковый номер завершенного запроса и заполняет поля в структуре status. Запрос становится не активным и дескриптор запроса устанавливается в MPI_REQUEST_NULL.

Массив array_of_request может содержать нулевые значения. В этом случае вызов заканчивается немедленно с index =MPI_UNDEFINED и со статусом empty.

MPI_Testany (count, array_of_requests, index, flag, status)

IN count длина списка (целое)

IN/OUT array_of_requests массив запросов (массив дескрипторов)

OUT index индекс дескриптора для завершенной операции (целое)

OUT flag true, если одна из операций завершена

OUT status статусный объект (статус)

MPI_Testany не блокирует работу процесса, выполняет тестирование завершенных операций обмена из указанного массива дескрипторов запроса. Если любая из указанных операций обмена завершена, функция устанавливает flag = true, индекс запроса присваивается параметру index и заполняются поля в структуре status; запрос удаляется и дескриптор запроса устанавливается в MPI_REQUEST_NULL. Если ни одна операция приема/передачи с поданным массивом запросов не завершилась на момент вызова функции MPI_Testany, возвращается flag = false, значение MPI_UNDEFINED в index и состояние аргумента status является неопределенным. Массив array_of_request может содержать нулевые значения. В этом случае вызов заканчивается немедленно с flag = true, index = MPI_UNDEFINED и status = empty.

MPI_Waitall ( count, array_of_requests, array_of_statuses)

IN count длина списков (целое)

INOUT array_of_requests массив запросов (массив дескрипторов)

OUT array_of_statuses массив статусных объектов (массив статусов)

MPI_Waitall блокирует работу, пока все операции обмена, связанные с дескрипторами в указанном массиве запросов не завершатся. Массив запросов и массив статусов должны иметь одинаковое количество элементов. Если завершение какого либо обмена в MPI_Waitall, оказался неудачным, функция возвращает код MPI_ERR_IN_STATUS и устанавливает в поля ошибки каждого статуса специфический код. Код ошибки будет MPI_SUCCESS, если все обмены завершены удачно. Если в MPI_Waitall подать неверный параметр, то обмен не будет завершен.

MPI_Testall (count, array_of_requests, flag, array_of_statuses)

IN count длина списка (целое)

INOUT array_of_requests массив запросов (массив дескрипторов)

OUT flag (логический тип)

OUT array_of_statuses массив статусных объектов(массив статусов)

MPI_Testall не блокирует работу процесса, возвращает flag=true, если на момент вызова функции все обмены, указанные в массиве дескрипторов запросов завершены. В этом случае заполняется каждый статусный элемент из массива array_of_statuses, который соответствует каждому завершенному обмену. Запрос удаляется и дескриптор устанавливается в MPI_REQUEST_NULL. В противном случае возвращается flag=false.

MPI_Waitsome(incount, array_of_requests, outcount, array_of_indices, array_of_statuses)

IN incount длина массива запросов (целое)

INOUT array_of_requests массив запросов (массив дескрипторов)

OUT outcount число завершенных запросов (целое)

OUT array_of_indices массив индексов операций, которые завершены (массив целых)

OUT array_of_statuses (массив статусов)

MPI_Waitsome блокирует работу процесса до тех пор, пока хотя бы одна операция, связанная с указанным массивом дескрипторов запроса, не завершится. Возвращает в outcount количество завершенных запросов. В массив array_of_indices присваиваются индексы завершенных операций (значения в array_of_indices соответствуют индексу завершенного обмена в массиве array_of_requests). В массив array_of_status возвращает статус завершенных операций. Завершенные запросы удаляются, связанные с ними дескрипторы устанавливаются в MPI_REQUEST_NULL.

MPI_Testsome(incount, array_of_requests, outcount, array_of_indices, array_of_statuses)

IN incount длина массива запросов (целое)

IN OUT array_of_requests массив запросов (массив дескрипторов)

OUT outcount число завершенных запросов (целое)

OUT array_of_indices массив индексов завершенных операций (массив целых)

OUT array_of_statuses (массив статусов)

MPI_Testsome не блокирует работу процесса, выполняется аналогично MPI_Waitsome. Если ни одной операции на момент вызова функции не завершено, outcount = 0.

MPI_Waitsome будет выполняться, если в списке содержится хотя бы один активный дескриптор. Функции завершения обменов требуют однозначности операций приема-передачи. Например, если запрос на окончание приема выполнен, должен быть выполнен и запрос на окончание соответствующей передачи. Только в этом случае обмен будет завершен успешно.

/////// MPI_Isend process 0 -> MPI_Irecv all process ///////////

/////// program run : mpirun -np 4 file_name.exe ///////////

#include <iostream.h>

#include <mpi.h>

#define NumProcForPrintResult 1

void main( int argc, char *argv[] ) {

int proc, size;

int i, j=0;

double a[10], b[10];

MPI_Status *stat;

MPI_Request *req;

MPI_Init( &argc, &argv);

MPI_Comm_rank( MPI_COMM_WORLD, &proc);

MPI_Comm_size( MPI_COMM_WORLD, &size);

if (proc == 0) { /* код для proc=0 */

stat=new MPI_Status [6];

req=new MPI_Request [6];

for(i=0; i<10; i++)

a[i]=(double)i+1.2;

MPI_Irecv(&a[0], 10, MPI_DOUBLE, 0, 99, MPI_COMM_WORLD, &req[0]);

/* MPI_Wait(&req[0],&stat[0]); //ошибка!!!!! блокировка */

/* for(i=0; i<10; i++) //возможна ошибка, передача, возможно

cout<<b[i])<<” “; // не закончилась принимающие массивы без значение

*/

for(i=0,j=1; i<size; i++,j++)

MPI_Isend(&a[0], 10, MPI_DOUBLE, i, 99, MPI_COMM_WORLD, &req[j]);

MPI_Waitall(5,req,stat);

}

else { /* код для остальных процессов */

stat=new MPI_Status [2];

req=new MPI_Request [2];

MPI_Irecv(&b[0], 10, MPI_DOUBLE, 0, 99, MPI_COMM_WORLD, &req[0]);

MPI_Wait(&req[0],&stat[0]);

}

if(proc==NumProcForPrintResult) {

for(i=0; i<10; i++)

cou<<b[i]<<” “;

cout<<endl;

}

MPI_Finalize();

}

Семантика не блокирующих коммуникаций

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

Функции проверки и отмены сообщений

Если Вы не знаете, какой объем информации должен принять процесс, следовательно, Вы не можете в функции приема - MPI_Recv, правильно задать количество принимаемых данных, подать массив приема сообщения необходимого размера, поскольку очевидно, что Вы не знаете, какой объем памяти Вам необходимо предварительно выделить под принимающий массив.

Для того, чтобы избежать возможных ошибок в таких ситуациях, а также с целью экономии памяти в MPI реализованы функции MPI_Probe и MPI_Iprobe.

Функции MPI_Probe и MPI_Iprobe позволяют проверить входные сообщения по номеру источника отправления (source) без их реального приема (до вызова функции MPI_Recv(…)). Пользователь затем может решить, как ему принимать эти сообщения, основываясь на информации, полученной в структуре status. В частности, пользователь может выделить память для приемного буфера согласно длине пришедшего сообщения.

MPI_Iprobe (source, tag, comm, flag, status)

IN source номер процесса-отправителя или MPI_ANY_SOURCE

IN tag значение тэга или MPI_ANY_TAG

IN comm коммуникатор

OUT flag (логическое значение)

OUT status статус (статус)

MPI_Probe (source, tag, comm, status)

IN source номер источника или MPI_ANY_SOURCE

IN tag значение тэга или MPI_ANY_TAG

IN comm коммуникатор

OUT status статус (статус)

Функция MPI_Probe является блокирующей и заканчивается после того, как соответствующее сообщение было найдено. Функция MPI_Iprobe является не блокирующей. Она просматривает всю очередь пришедших сообщений и возвращает значение flag = true, если сообщение с заданными в функции атрибутами, соответствующими аргументам source, tag и comm доставлено. В этом случае поля структуры status, которая является аргументом функции, инициализируются значениями, в соответствии с пробованным сообщением. В противном случае вызов возвращает flag = false, и оставляет status неопределенным.

Все пришедшие сообщения выстраиваются в очередь до тех пор, пока не будет вызвана функция приема MPI_Recv, которая выбирает из очереди по заданным в функции атрибутам соответствующее сообщение. Сообщение принимается и системные ресурсы, используемые для данного обмена, освобождаются. Если в функциях MPI_Probe и MPI_Iprobe используются MPI_ANY_SOURCE и MPI_ANY_TAG, будет получена информация о первом пришедшем сообщении. Определить номер процесса-отправителя, значение tag и длины сообщения можно, обращаясь к полям структуры status. При использовании MPI_ANY_SOURCE и tag будет получена информация о первом пришедшем сообщении с данным значением tag. При использовании MPI_ANY_TAG и source будет получена информация о первом пришедшем сообщении от процесса с номером source.

Аргумент status также возвращает информацию о длине принятого сообщения. Однако, эта информация не содержится как поле структуры status; чтобы получить значение длины принятого сообщения необходимо вызвать функцию MPI_GET_COUNT.

MPI_GET_COUNT(status, datatype, count)

IN

status

статус, возвращенный операцией приема (Status)

IN

datatype

тип принимаемых данных (handle)

OUT

count

количество принятых элементов (integer)

Операция MPI_Cancel маркирует для отмены ждущие выполнения не блокирующие операции обмена по указанному в параметре дескриптору запроса.

MPI_Cancel (request)

IN request - коммуникационный запрос (дескриптор)

Вызов MPI_Cancel заканчивается немедленно. После маркировки отмены операции обмена необходимо ее завершить, используя вызов MPI_Wait или MPI_Test (или любые производные операции). Если обмен отмечен для отмены, то вызов MPI_Wait для этой операции гарантирует ее завершение.

Если используется буферизующий обмен (MPI_Ibsend), будет освобожден буфер, выделенный для обмена.

Отмена операции обмена и выполнение обмена одновременно выполняться не могут, либо выполняется обмен, либо выполняется его отмена. Если операция была отменена, то информация о произведенном действии будет возвращена в аргументе status той операции, которая выполняет обмен.

MPI_TEST_CANCELLED(status, flag)

Возвращает flag = true, если обмен, связанный с объектом статуса, был успешно отменён. В этом случае, все другие поля статуса (такие как count и tag) не определены. В противном случае возвращается flag = false. Если операция приёма могла быть отменена, тогда следует вначале вызвать MPI_TEST_CANCELLED, чтобы проверить была ли операция отменена, перед проверкой других полей возвращенного статуса.

Совмещение передачи и приема сообщений

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

MPI_ Sendrecv (sendbuf, sendcount, sendtype, dest, sendtag, recvbuf, recvcount, recvtype, source, recvtag, comm, status)

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_replace(buf, count, type, dest, sendtag, source, recvtag, comm, status)

Имеет один и тот же буфер передачи и приема сообщения.

Лекция 4

Интерфейс MPI. Коллективные операции.

К операциям коллективного обмена относятся:

1 Барьерная синхронизация всех процессов группы MPI_Barrier(comm )

2.Широковещательная передача от одного процесса всем остальным процессам группы MPI_Bcast(buffer, count, datatype, root, comm )

3 Операция сбора данных

3.1 Сбор данных из всех процессов группы в один процесс

MPI_Gather(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, int root, comm);

3.2 Сбор данных из всех процессов группы во все процессы группы

MPI_Allgather(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm);

3.3 Сбор данных из всех процессов группы в один процесс со смещением.

MPI_Gatherv(sendbuf, sendcount, sendtype, recvbuf, int *recvcount, int *displs, recvtype, root, comm);

3.4 Сбор данных из всех процессов группы во все процессы группы со смещением

MPI_Allgatherv(sendbuf, sendcount, sendtype, recvbuf, recvcounts, int *displs, recvtype, comm);

4 Операция рассылки данных

4.1Рассылка данных из одного процесса группы на все процессы группы

MPI_Scatter(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, comm);

4.2Рассылка данных из одного процесса группы на все процессы группы

MPI_Scatterv(sendbuf, sendcount, int *displs, sendtype, recvbuf, recvcount, recvtype, root, comm);

5. Операции редукции (обработки данных), выполняют передачу данных с последующим выполнением заданной операции, например: сложение, нахождение максимума/минимума и т.д. (операции может создавать программист). Результатом выполнения функции будет результат выполнения операции. Более подробно операцию рассмотрим позже.

MPI_Reduce(sendbuf, recvbuf, count, datatype, MPI_Op op, root, comm); - результат находится на процессе root.

MPI_Allreduce(sendbuf, recvbuf, count, datatype, MPI_Op op, comm); - результат находится на всех процессах группы.

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

Вызов коллективной функции может возвращать управление сразу, как только его участие в коллективной операции завершено.