Модели и средства программирования для многопроцессорных вычислительных систем

Интерфейс передачи сообщений MPI

Разбить на страницы
Показывать лекцию целиком

Далее приводится описание свободно распространяемой версии MPI - MPICH 1.2.7, соответствующей спецификации MPI 1.

Модель параллельной программы в MPI

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

Каждому процессу в области взаимодействия назначается уникальный числовой идентификатор - ранг,значение которого от 0 до np - 1 ( np - число процессов). Ранги, назначаемые одному и тому же процессу в разных коммуникаторах, вообще говоря, различны.

Структура программы, написанной по схеме хозяин/работник, приведена ниже.

program para
...
if  (ранг процесса = рангу мастер-процесса)  then
код мастер-процесса else
код подчиненного процесса (подчиненных процессов)
endif
end

Каждый экземпляр программы уже в процессе своего выполнения определяет, является ли он мастер-процессом. Затем, в зависимости от результата этой проверки, выполняется одна из ветвей условного оператора. Первая ветвь соответствует мастер-задаче, а вторая - подчиненной задаче. Способы взаимодействия между подзадачами определяются программистом.

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

Сообщения

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

  • ранг процесса-отправителя сообщения;
  • адрес, по которому размещаются пересылаемые данные процесса-отправителя;
  • тип пересылаемых данных;
  • количество данных;
  • ранг процесса, который должен получить сообщение;
  • адрес, по которому должны быть размещены данные процессом-получателем.
  • тег сообщения;
  • идентификатор коммуникатора, описывающего область взаимодействия, внутри которой происходит обмен.
  • Тег - это задаваемое пользователем целое число от 0 до 32767, которое играет роль идентификатора сообщения и позволяет различать сообщения, приходящие от одного процесса. Теги могут использоваться и для соблюдения определенного порядка приема сообщений.

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

    Разновидности обменов сообщениями

    В MPI реализованы разные виды обменов. Прежде всего, это двухточечные (задействованы только два процесса) и коллективные (задействованы более двух процессов).

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

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

  • Привязка к языку Fortran

    Имена подпрограмм и констант MPI в программах на языке Fortran начинаются с MPI _. При вызове подпрограмм коды завершения передаются через дополнительный параметр целого типа (находится на последнем месте в списке параметров подпрограммы). Код успешного завершения - MPI_SUCCESS. Константы и другие объекты MPI описываются в файле mpif.h, который включается в MPI -программу с помощью оператора include.

    В некоторых подпрограммах используется переменная status, которая является массивом стандартного целого типа. Его размер MPI_STATUS_SIZE.

    При обращении к подпрограммам MPI используются типы данных MPI, для большинства из которых имеется соответствие базовым типам языка (см. табл. 3.1)

    Типы MPI_Datatype и MPI_Comm - эмулируются стандартным целым типом языка Fortran (Integer).

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

    Типы данных MPI для языка Fortran
    Тип данных MPI Тип данных Fortran
    MPI_INTEGER Integer
    MPI_REAL Real
    MPI_DOUBLE_PRECISION Double precision
    MPI_DOUBLE_COMPLEX Double complex
    MPI_COMPLEX Complex
    MPI_LOGICAL Logical
    MPI_CHARACTER Character
    MPI_BYTE Нет соответствия
    MPI_PACKED Нет соответствия
    Типы, которые имеются не во всех реализациях MPI
    MPI_INTEGER1 Integer*1
    MPI_INTEGER2 Integer*2
    MPI_INTEGER4 Integer*4
    MPI_REAL4 Real*4
    MPI_REAL8 Real*8

    Привязка к языку C

    В программах на языке C имена подпрограмм имеют вид Класс_действие_подмножество или Класс_действие. В C++ подпрограмма является методом для определенного класса, имя имеет в этом случае вид MPI::Класс::действие_подмножество. Для некоторых действий введены стандартные наименования: Create - создание нового объекта, Get - получение информации об объекте, Set - установка параметров объекта, Delete - удаление информации, Is - запрос о том, имеет ли объект указанное свойство.

    Имена констант MPI записываются в верхнем регистре. Их описания находятся в заголовочном файле mpi.h.

    Входные параметры функций передаются по значению, а выходные (и INOUT ) - по ссылке. Соответствие типов MPI стандартным типам языка C приведено в табл. 3.2.

    Коды завершения

    В MPI приняты стандартные соглашения о кодах завершения вызовов подпрограмм. Так, например, возвращаются значения MPI_SUCCESS - при успешном завершении вызова и MPI_ERR_OTHER - обычно при попытке повторного вызова процедуры MPI_Init.

    Вместо числовых кодов в программах обычно используют специальные именованные константы:

  • MPI_ERR_BUFFER - неправильный указатель на буфер;
  • MPI_ERR_COMM - неправильный коммуникатор;
  • MPI_ERR_RANK - неправильный ранг;
  • MPI_ERR_OP - неправильная операция;
  • MPI_ERR_ARG - неправильный аргумент;
  • MPI_ERR_UNKNOWN - неизвестная ошибка;
  • MPI_ERR_TRUNCATE - сообщение обрезано при приеме;
  • MPI_ERR_INTERN - внутренняя ошибка. Обычно возникает, если системе не хватает памяти.
  • Типы данных MPI для языка С
    Тип данных MPI Тип данных C
    MPI_CHAR Signed char
    MPI_SHORT Signed short int
    MPI_INT Signed int
    MPI_LONG Signed long int
    MPI_UN SIGNE D_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

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

  • MPI_COMM_WORLD - включает все процессы параллельной программы;
  • MPI_COMM_SELF - включает только данный процесс;
  • MPI_COMM_NULL - пустой коммуникатор, не содержит ни одного процесса.
  • В MPI имеются процедуры, позволяющие создавать новые коммуникаторы, содержащие подмножества процессов.

    (рис 3.1) Коммуникатор

    Ранг процесса представляет собой уникальный числовой идентификатор, назначаемый процессу в том или ином коммуникаторе. Ранги в разных коммуникаторах назначаются независимо и имеют целое значение от 0 до число_процессов - 1 (рис. 3.2).

    (рис 3.2) Ранги процессов

    Тег (маркер) сообщения - это уникальный числовой идентификатор, который назначается сообщению и позволяет различать сообщения, если в этом есть необходимость. Если тег не требуется, вместо него можно использовать "джокер" MPI_ANY_TAG.

    Типовая структура MPI -программы:

    program para
    ...
    if (process = master) then
    master clause else
    slave clause endif end

    Различные подпрограммы MPI

    Подключение к MPI

    int MPI_Init(int *argc, char **argv)
    
    MPI_INIT(IERR)

    Аргументы argc и argv требуются только в программах на C, где они задают количество аргументов командной строки запуска программы и вектор этих аргументов. Данный вызов предшествует всем прочим вызовам подпрограмм MPI.

    Завершение работы с MPI

    p>int MPI_Finalize()
    
    MPI_FINALIZE(IERR)

    После вызова данной подпрограммы нельзя вызывать подпрограммы MPI. MPI_FINALIZE должны вызывать все процессы перед завершением своей работы.

    Определение размера области взаимодействия

    p>int MPI_Comm_size(MPI_Comm comm, int *size)
    MPI_COMM_SIZE(COMM., SIZE, IERR)

    Входные параметры:

  • comm - коммуникатор.
  • Выходные параметры:

  • size - количество процессов в области взаимодействия.
  • Определение ранга процесса

    int MPI_Comm_rank(MPI_Comm comm, int *rank)
    MPI_COMM_RANK(COMM, RANK, IERR)

    Входные параметры:

  • comm- коммуникатор.
  • Выходные параметры:

  • rank - ранг процесса в области взаимодействия.
  • Определение имени узла, на котором выполняется данный процесс

    MPI_Get_processor_name(char *name, int *resultlen)
    
    MPI_GET_PROCESSOR_NAME(NAME, RESULTLEN, IERR)

    Выходные параметры:

  • name - идентификатор вычислительного узла. Массив не менее чем из MPI_MAX_PROCESSOR_NAME элементов;
  • resultlen - длина имени.
  • Время, прошедшее с произвольного момента в прошлом

    double MPI_Wtime()
    
    MPI_WTIME(TIME,   IERR)

    Двухточечный обмен

    Участниками двухточечного обмена являются два процесса: процесс-отправитель и процесс-получатель (рис. 3.3).

    (рис 3.3) Двухточечный обмен

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

    Стандартная блокирующая передача

    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, IERR)

    Входные параметры:

  • buf - адрес первого элемента в буфере передачи;
  • count - количество элементов в буфере передачи;
  • datatype - тип MPI каждого пересылаемого элемента;
  • dest - ранг процесса-получателя сообщения (целое число от 0 до n - 1, где n - число процессов в области взаимодействия);
  • tag - тег сообщения;
  • comm - коммуникатор;
  • ierr - код завершения.
  • Стандартный блокирующий прием

    int MPI_Recv(void *buf,  int count, MPI_Datatype datatype,  int source, 
    	int tag, MPI_Comm comm, MPI_Status *status)
    MPI_RECV(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, STATUS, IERR)

    Входные параметры:

  • count - максимальное количество элементов в буфере приема. Фактическое их количество можно определить с помощью подпрограммы MPI_Get_count ;
  • datatype - тип принимаемых данных. Напомним о необходимости соблюдения соответствия типов аргументов подпрограмм приема и передачи;
  • source - ранг источника. Можно использовать специальное значение MPI_ANY_SOURCE, соответствующее произвольному значению ранга. В программировании идентификатор, отвечающий произвольному значению параметра, часто называют "джокером". Этот термин будем использовать и мы;
  • tag - тег сообщения или "джокер" MPI_ANY_TAG, соответствующий произвольному значению тега;
  • comm - коммуникатор. При указании коммуникатора "джокеры" использовать нельзя.
  • Выходные параметры:

  • buf - начальный адрес буфера приема. Его размер должен быть достаточным, чтобы разместить принимаемое сообщение, иначе при выполнении приема произойдет сбой - возникнет ошибка переполнения;
  • status - статус обмена.
  • Если сообщение меньше, чем буфер приема, изменяется содержимое лишь тех ячеек памяти буфера, которые относятся к сообщению.

    Определение размера полученного сообщения (count)

    int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count)
    
    MPI_GET_COUNT(STATUS, DATATYPE, COUNT, IERR)

    Аргумент datatype должен соответствовать типу данных, указанному в операции передачи сообщения.

    Синхронная передача

    int MPI_Ssend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
    
    MPI_SSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERR)

    Параметры этой подпрограммы совпадают с параметрами подпрограммы MPI_Send.

    Буферизованный обмен

    int MPI_Bsend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
    
    MPI_BSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERR)

    Параметры совпадают с параметрами подпрограммы MPI_Send.

    Создание буфера

    int MPI_Buffer_attach(void *buf,  size)
    
    MPI_BUFFER_ATTACH(BUF, SIZE, IERR)

    Выходной параметр:

  • buf - буфер размером size байтов.
  • В программах на языке Fortran роль буфера может играть массив. Этот массив должен быть описан в программе, его не следует использовать для других целей (например, в качестве первого аргумента подпрограммы MPI_Bsend ). За один раз к процессу может быть подключен только один буфер.

    Отключение буфера

    int MPI_Buffer_detach(void *buf, int *size)
    MPI_BUFFER_DETACH(BUF, SIZE, IERR)

    Выходные параметры:

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

    Передача по готовности

    int MPI_Rsend(void *buf, int count, MPI_Datatype datatype, 
    	int dest, int tag,  MPI_Comm comm)
    
    MPI_RSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERR)

    Параметры совпадают с параметрами подпрограммы MPI_Send.

    Блокирующая проверка доставки сообщения

    int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status)
    
    MPI_PROBE(SOURCE, TAG, COMM, STATUS, IERR)

    Входные параметры:

  • source - ранг источника или " джокер";
  • tag - значение тега или "джокер";
  • comm - коммуникатор.
  • Выходной параметр:

  • status - статус.
  • Неблокирующая проверка сообщения

    int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag, MPI_Status *status)
    
    MPI_IPROBE(SOURCE, TAG, COMM, FLAG, STATUS, IERR)

    Входные параметры этой подпрограммы те же, что и у подпрограммы MPI_Probe

    Выходные параметры:

  • flag - флаг;
  • status - статус.
  • Если сообщение уже поступило и может быть принято, возвращается значение флага "истина".

    Прием и передача данных с блокировкой

    int MPI_Sendrecv(void *sendbuf,  int sendcount, MPI_Datatype sendtype, 
    	int dest,  int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype,  
    	int source,  int recvtag, MPI_Comm comm, MPI_Status *status)
    MPI_SENDRECV(SENDBUF, SENDCOUNT, SENDTYPE, DEST, SENDTAG, 
    	RECVBUF, RECVCOUNT, RECVTYPE, SOURCE, RECVTAG, COMM, STATUS, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • sendcount - количество передаваемых элементов;
  • sendtype - тип передаваемых элементов;
  • dest - ранг адресата;
  • sendtag - тег передаваемого сообщения;
  • recvbuf - начальный адрес буфера приема;
  • recvcount - количество элементов в буфере приема;
  • recvtype - тип элементов в буфере приема;
  • source - ранг источника;
  • recvtag - тег принимаемого сообщения;
  • comm - коммуникатор.
  • Выходные параметры:

  • recvbuf - начальный адрес буфера приема;
  • status - статус операции приема.
  • Прием, и передача используют один и тот же коммуникатор. Буферы передачи и приема не должны пересекаться, у них может быть разный размер, типы пересылаемых и принимаемых данных также могут различаться.

    Отправка и прием сообщения в блокирующем режиме с общим буфером для передачи и для приема

    int MPI_Sendrecv_replace(void *buf,  int count, MPI_Datatype datatype, 
    	int dest,  int sendtag,  int source,  int recvtag, MPI_Comm comm, MPI_Status *status)
    
    MPI_SENDRECV_REPLACE(BUF, COUNT, DATATYPE, DEST, SENDTAG, SOURCE, RECVTAG, COMM, STATUS, IERR)

    Входные параметры:

  • count - количество отправляемых данных и емкость буфера приема;
  • datatype - тип данных в буфере приема и передачи;
  • dest - ранг адресата;
  • sendtag - тег передаваемого сообщения;
  • source - ранг источника;
  • recvtag - тег принимаемого сообщения;
  • comm - коммуникатор.
  • Выходные параметры:

  • buf - начальный адрес буфера приема и передачи;
  • status - статус.
  • Принимаемое сообщение не должно превышать по размеру отправляемое сообщение, а передаваемые и принимаемые данные должны быть одного типа. Последовательность приема и передачи выбирается системой автоматически.

    Инициализация неблокирующей стандартной передачи

    int MPI_Isend(void *buf,  int count, MPI_Datatype datatype,  
    	int dest, int tag, MPI_Comm comm, MPI_Request *request)
    MPI_ISEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Входные параметры этой подпрограммы аналогичны аргументам подпрограммы MPI_Send.

    Выходной параметр:

  • request - идентификатор операции.
  • Инициализация неблокирующей синхронной передачи данных

    int MPI_Issend(void *buf, int count, MPI_Datatype datatype, 
    	int dest, int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_ISSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Параметры этой подпрограммы совпадают с параметрами подпрограммы MPI_Send.

    Неблокирующая буферизованная передача сообщения

    int MPI_Ibsend(void *buf, int count, MPI_Datatype datatype, int dest, 
    	int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_IBSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Неблокирующая передача "по готовности"

    int MPI_Irsend(void* buf, int count, MPI_Datatype datatype, int dest, 
    	int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_IRSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Параметры всех подпрограмм неблокирующей передачи совпадают.

    Инициализация неблокирующего приема

    int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, 
    int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_IRECV(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, REQUEST, IERR)

    Назначение аргументов здесь такое же, как и в предыдущих подпрограммах, за исключением того, что указывается ранг не адресата, а источника сообщения ( source ).

    Блокировка работы процесса до завершения приема или передачи сообщения

    int MPI_Wait(MPI_Request *request, MPI_Status *status)
    MPI_WAIT(REQUEST, STATUS, IERR)

    Входной параметр:

  • request - идентификатор операции обмена.
  • Выходной параметр:

  • status - статус выполненной операции.
  • Значение статуса для операции передачи сообщения можно получить вызовом подпрограммы MPI_Test_cancelled. Можно вызвать MPI_Wait с пустым или неактивным аргументом request. В этом случае операция завершается сразу же с пустым статусом.

    Успешное выполнение подпрограммы MPI_Wait после вызова MPI_Ibsend подразумевает, что буфер передачи можно использовать вновь, то есть пересылаемые данные отправлены или скопированы в буфер, выделенный при вызове подпрограммы MPI_Buffer_attach. В этот момент уже нельзя отменить передачу. Если не будет зарегистрирован соответствующий прием, буфер нельзя будет освободить. В этом случае можно применить подпрограмму MPI_Cancel, которая освобождает память, выделенную подсистеме коммуникаций.

    Неблокирующая проверка завершения приема или передачи сообщения

    int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status)
    
    MPI_TEST(REQUEST, FLAG, STATUS, IERR)

    Входной параметр:

  • request - идентификатор операции обмена.
  • Выходные параметры:

  • flag - "истина", если операция, заданная идентификатором request, выполнена;
  • status - статус выполненной операции.
  • Если при вызове MPI_Test используется пустой или неактивный аргумент request, операция возвращает значение флага "истина" и пустой статус.

    Проверка завершения всех обменов

    int MPI_Waitall(int count, MPI_Request requests[], MPI_Status statuses [ ])
    MPI_WAITALL(COUNT, REQUESTS, STATUSES, IERR)

    Выполнение процесса блокируется до тех пор, пока все операции обмена, связанные с активными запросами в массиве requests, не будут выполнены. Возвращается статус этих операций. Статус обменов содержится в массиве statuses. count - количество запросов на обмен (размер массивов requests и statuses ).

    В результате выполнения подпрограммы MPI_Waitall запросы, сформированные неблокирующими операциями обмена, аннулируются, а соответствующим элементам массива присваивается значение MPI_REQUEST_NULL. Список может содержать пустые или неактивные запросы. Для каждого из них устанавливается пустое значение статуса.

    В случае неуспешного выполнения одной или более операций обмена подпрограмма MPI_Waitall возвращает код ошибки MPI_ERR_IN_STATUS и присваивает полю ошибки статуса значение кода ошибки соответствующей операции. Если операция выполнена успешно, полю присваивается значение MPI_SUCCESS, а если не выполнена, но и не было ошибки - значение MPI_ERR_PENDING. Последний случай соответствует наличию запросов на выполнение операции обмена, ожидающих обработки.

    Неблокирующая проверка завершения обменов

    int MPI_Testall(int count, MPI_Request requests[], int *flag, MPI_Status statuses[])
    MPI_TESTALL(COUNT, REQUESTS, FLAG, STATUSES, IERR)

    При вызове возвращается значение флага ( flag ) "истина", если все обмены, связанные с активными запросами в массиве requests, выполнены. Если завершены не все обмены, флагу присваивается значение "ложь", а массив statuses не определен. count - количество запросов.

    Каждому статусу, соответствующему активному запросу, присваивается значение статуса соответствующего обмена. Если запрос был сформирован операцией неблокирующего обмена, он аннулируется, а соответствующему элементу массива присваивается значение MPI_REQUEST_NULL. Каждому статусу, соответствующему пустому или неактивному запросу, присваивается пустое значение.

    Блокирующая проверка завершения любого числа обменов

    int MPI_Waitany(int count, MPI_Request requests[], int *index, MPI_Status *status)
    
    MPI_WAITANY(COUNT, REQUESTS, INDEX, STATUS, IERR)

    Выполнение процесса блокируется до тех пор, пока, по крайней мере, один обмен из массива запросов ( requests ) не будет завершен.

    Входные параметры:

  • requests - запрос;
  • count - количество элементов в массиве requests, а выходные: status и index .
  • Выходные параметры:

  • index - индекс запроса (в языке C это целое число от 0 до count - 1, а в языке FORTRAN от 1 до count ) в массиве requests ;
  • status - статус.
  • Если запрос на выполнение операции был сформирован неблокирующей операцией обмена, он аннулируется и ему присваивается значение MPI_REQUEST_NULL. Массив запросов может содержать пустые или неактивные запросы. Если в списке вообще нет активных запросов или он пуст, вызовы завершаются сразу со значением индекса MPI_UNDEFINED и пустым статусом.

    Проверка выполнения любого ранее инициализированного обмена

    int MPI_Testany(int count, MPI_Request requests[],  int *index, 
    	int *flag, MPI_Status *status)
    MPI_TESTANY(COUNT, REQUESTS, INDEX, FLAG, STATUS, IERR)

    Смысл и назначение параметров этой подпрограммы те же, что и для подпрограммы MPI_Waitany. Дополнительный аргумент flag, который принимает значение "истина", если одна из операций завершена. Блокирующая подпрограмма MPI_Waitany и неблокирующая MPI_Testany взаимозаменяемы, впрочем, как и другие аналогичные пары.

    Подпрограммы MPI_Waitsome и MPI_Testsome действуют аналогично подпрограммам MPI_Waitany и MPI_Testany, кроме случая, когда завершается более одного обмена. В подпрограммах MPI_Waitany и MPI_Testany обмен из числа завершенных выбирается произвольно, именно для него и возвращается статус, а для MPI_Waitsome и MPI_Testsome статус возвращается для всех завершенных обменов. Эти подпрограммы можно использовать для определения, сколько обменов завершено:

    int MPI_Waitsome(int incount, MPI_Request requests[],  int *outcount, 
    	int indices[], MPI_Status statuses[])
    MPI_WAITSOME(INCOUNT, REQUESTS, OUTCOUNT, INDICES, STATUSES, IERR)

    Здесь incount - количество запросов. В outcount возвращается количество выполненных запросов из массива requests, а в первых outcount элементах массива indices возвращаются индексы этих операций. В первых outcount элементах массива statuses возвращается статус завершенных операций. Если выполненный запрос был сформирован неблокирующей операцией обмена, он аннулируется. Если в списке нет активных запросов, выполнение подпрограммы завершается сразу, а параметру outcount присваивается значение MPI_UNDEFINED.

    Неблокирующая проверка выполнения обменов

    int MPI_Testsome(int incount, MPI_Request requests[],  int *outcount, 
    	int indices[], MPI_Status statuses[])
    
    MPI_TESTSOME(INCOUNT, REQUESTS, OUTCOUNT, INDICES, STATUSES, IERR)

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

    Запрос для стандартной передачи создается при вызове подпрограммы MPI_Send_init:

    int MPI_Send_init(void *buf,  int count, MPI_Datatype datatype, 
    	int dest,  int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_SEND_INIT(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Входные параметры:

  • buf - адрес буфера передачи;
  • count - количество элементов;
  • datatype - тип элементов;
  • dest - ранг адресата;
  • tag - тег сообщения;
  • comm - коммуникатор.
  • Выходной параметр:

  • request - запрос на выполнение операции обмена.
  • Инициализация отложенного обмена

    int MPI_Start(MPI_Request *request)
    MPI_START(REQUEST, IERR)

    Входной параметр:

  • request - запрос на выполнение операции обмена.
  • Вызов MPI_Start с запросом на обмен, созданным MPI_Send_init, инициирует обмен с теми же свойствами, что и вызов подпрограммы MPI_Isend, а вызов MPI_Start с запросом, созданным MPI_Bsend_init, инициирует обмен аналогично вызову MPI_Ibsend. Сообщение, которое передано операцией, инициированной с помощью MPI_Start, может быть принято любой подпрограммой приема.

    Инициализация обменов, связанных с запросами на выполнение неблокирующей операции обмена в массиве requests

    int MPI_Startall(int count, MPI_request *requests)
    
    MPI_STARTALL(COUNT, REQUESTS, IERR)

    Аннулирование неблокирующих "ждущих" (ожидающих обработки) обменов

    int MPI_Cancel(MPI_request *request)
    
    MPI_CANCEL(REQUEST, IERR)

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

    Проверка, произведено ли аннулирование обмена, связанного с указанным статусом

    int MPI_Test_cancelled(MPI_Status *status,  int *flag)
    
    MPI_TEST_CANCELLED(STATUS, FLAG, IERR)

    Аннулирование запроса на выполнение операции (request)

    int MPI_Request_free(MPI_Request *request)
    
    MPI_REQUEST_FREE(REQUEST, IERR)

    При вызове помечает запрос на обмен для удаления и присваивает ему значение MPI_REQUEST_NULL. Операции обмена, связанной с этим запросом, дается возможность завершиться, а сам запрос удаляется только после завершения обмена.

    Коллективный обмен

    Участниками коллективного обмена являются более двух процессов.

    Широковещательная рассылка

    int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
    MPI_BCAST(BUFFER, COUNT, DATATYPE, ROOT, COMM, IERR)

    Параметры этой процедуры одновременно являются входными и выходными:

  • buffer - адрес буфера;
  • count - количество элементов данных в сообщении;
  • datatype - тип данных MPI ;
  • root - ранг главного процесса, выполняющего широковещательную рассылку;
  • comm - коммуникатор.
  • Схема распределения данных представлена на рис. 3.4.

    (рис 3.4) Распределение данных при широковещательной рассылке

    Синхронизация с помощью "барьера" (рис. 3.5)

    int MPI_Barrier(MPI_Comm comm)
    MPI_BARRIER(COMM, IERR)
    (рис 3.5) Синхронизация с "барьером"

    При синхронизации с барьером выполнение каждого процесса из данного коммуникатора приостанавливается до тех пор, пока все процессы не выполнят вызов процедуры синхронизации MPI_Barrier.

    Распределение данных

    int MPI_Scatter(void *sendbuf,  int sendcount, MPI_Datatype sendtype, void *rcvbuf,  int rcvcount, 
      MPI_Datatype rcvtype,  int root, MPI_Comm comm)
    MPI_SCATTER(SENDBUF,SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNT,   
    RCVTYPE, ROOT, COMM, IERR)

    Входные параметры:

  • sendbuf - адрес буфера передачи;
  • sendcount - количество элементов, пересылаемых каждому процессу (но не суммарное количество пересылаемых элементов);
  • sendtype - тип передаваемых данных;
  • rcvcount - количество элементов в буфере приема;
  • rcvtype - тип принимаемых данных;
  • root - ранг передающего процесса;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - адрес буфера приема.
  • Процесс с рангом root распределяет содержимое буфера передачи sendbuf среди всех процессов (рис. 3.6). Содержимое буфера передачи разбивается на несколько фрагментов, каждый из которых содержит sendcount элементов. Первый фрагмент передается процессу 0, второй процессу 1 и т. д. Аргументы send имеют значение только на стороне процесса root.

    (рис 3.6) Распределение данных при выполнении операции Scatter

    Сбор сообщений от остальных процессов в буфер главной задачи

    int MPI_Gather(void *sendbuf,  int sendcount, MPI_Datatype sendtype, void *rcvbuf,  
      int rcvcount, MPI_Datatype rcvtype,  int root, MPI_Comm comm)
    
    MPI_GATHER(SENDBUF, SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNT, RCVTYPE, ROOT, COMM, IERR)

    Каждый процесс в коммуникаторе comm пересылает содержимое буфера передачи sendbuf процессу с рангом root. Процесс root "склеивает" полученные данные в буфере приема (рис. 3.7). Порядок склейки определяется рангами процессов, то есть в результирующем наборе после данных от процесса 0 следуют данные от процесса 1, затем данные от процесса 2 и т. д. Аргументы rcvbuf, rcvcount и rcvtype играют роль только на стороне главного процесса. Аргумент rcvcount указывает количество элементов данных, полученных от каждого процесса (но не суммарное их количество). При вызове подпрограмм MPI_Scatter и MPI_Gather из разных процессов следует использовать общий главный процесс.

    (рис 3.7) Сбор данных при выполнении операции Gather

    Векторная подпрограмма распределения данных

    int MPI_Scatterv(void *sendbuf,  int *sendcounts,  int *displs, 
    	MPI_Datatype sendtype, void *rcvbuf, int rcvcount, 
    	MPI_Datatype rcvtype,  int root, MPI_Comm comm)
    
    MPI_SCATTERV(SENDBUF, SENDCOUNTS, DISPLS, SENDTYPE, RCVBUF, RCVCOUNT, RCVTYPE, ROOT, COMM, IERR)

    Входные параметры:

  • sendbuf - адрес буфера передачи;
  • sendcounts - целочисленный одномерный массив, содержащий количество элементов, передаваемых каждому процессу (индекс равен рангу адресата). Его длина равна количеству процессов в коммуникаторе;
  • displs - целочисленный массив, длина которого равна количеству процессов в коммуникаторе. Элемент с индексом i задает смещение относительно начала буфера передачи. Ранг адресата равен значению индекса i ;
  • sendtype - тип данных в буфере передачи;
  • rcvcount - количество элементов в буфере приема;
  • rcvtype - тип данных в буфере приема;
  • root - ранг передающего процесса;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - адрес буфера приема.
  • Сбор данных от всех процессов в заданном коммуникаторе и запись их в буфер приема с указанным смещением

    int MPI_Gatherv(void *sendbuf,  int sendcount, MPI_Datatype sendtype, void *recvbuf,  
      int *recvcounts,  int *displs, MPI_Datatype recvtype, int root, MPI_Comm comm)
    
    MPI_GATHERV(SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNTS, 
    	DISPLS, RECVTYPE, ROOT, COMM, IERR)

    Список параметров у этой подпрограммы похож на список параметров подпрограммы MPI_Scatterv. В обменах, выполняемых подпрограммами MPI_Allgather и MPI_Alltoall, нет главного процесса. Детали отправки и приема важны для всех процессов, участвующих в обмене.

    Сбор данных от всех процессов и распределение их всем процессам

    int MPI_Allgather(void *sendbuf,  int sendcount, MPI_Datatype sendtype, void *rcvbuf,  
      int rcvcount, MPI_Datatype rcvtype, MPI_Comm comm)
    
    MPI_ALLGATHER(SENDBUF, SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNT, RCVTYPE, COMM, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • sendcount - количество элементов в буфере передачи;
  • sendtype - тип передаваемых данных;
  • rcvcount - количество элементов, полученных от каждого процесса;
  • rcvtype - тип данных в буфере приема;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - адрес буфера приема.
  • Блок данных, переданный от j-го процесса, принимается каждым процессом и размещается в j-м блоке буфера приема recvbuf (рис. 3.8).

    (рис 3.8) Сбор и распределение данных при выполнении операции Allgather

    Пересылка данных по схеме "каждый - всем"

    int MPI_Alltoall(void *sendbuf,  int sendcount, MPI_Datatype sendtype, 
    	void *rcvbuf,  int rcvcount, MPI_Datatype rcvtype, MPI_Comm comm)
    
    MPI_ALLTOALL(SENDBUF, SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNT, RCVTYPE, COMM, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • sendcount - количество элементов данных, пересылаемых каждому процессу;
  • sendtype - тип данных в буфере передачи;
  • rcvcount - количество элементов данных, принимаемых от каждого процесса;
  • rcvtype - тип принимаемых данных;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - адрес буфера приема.
  • Векторными версиями MPI_Allgather и MPI_Alltoall являются подпрограммы MPI_Allgatherv и MPI_Alltoallv.

    Сбор данных от всех процессов и пересылка их всем процессам

    int MPI_Allgatherv(void *sendbuf,  int sendcount, MPI_Datatype sendtype, 
    	void *rcvbuf, int *rcvcounts, int *displs, MPI_Datatype rcvtype, MPI_Comm comm)
    
    MPI_ALLGATHERV(SENDBUF, SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNTS, DISPLS, RCVTYPE, COMM, IERR)

    Ее параметры совпадают с параметрами подпрограммы MPI_Allgather, за исключением дополнительного входного параметра displs. Это целочисленный одномерный массив, количество элементов в котором равно количеству процессов в коммуникаторе. Элемент массива с индексом i задает смещение относительно начала буфера приема recvbuf, в котором располагаются данные, принимаемые от процесса i. Блок данных, переданный от j-го процесса, принимается каждым процессом и размещается в j-м блоке буфера приема.

    Пересылка данных от всех процессов всем процессам со смещением

    int MPI_Alltoallv(void *sendbuf,  int *sendcounts,  int *sdispls, 
    	MPI_Datatype sendtype, void *rcvbuf,  int *rcvcounts,  int *rdispls, 
    	MPI_Datatype rcvtype, MPI_Comm comm)
    MPI_ALLTOALLV(SENDBUF, SENDCOUNTS, SDISPLS, SENDTYPE, RCVBUF, RCVCOUNTS, RDISPLS, RCVTYPE, COMM, IERR)

    Ее параметры аналогичны параметрам подпрограммы MPI_Alltoall, кроме двух дополнительных параметров:

  • sdispls - целочисленный массив, количество элементов в котором равно количеству процессов в коммуникаторе. Элемент j задает смещение относительно начала буфера, из которого данные передаются j -му процессу.
  • rdispls - целочисленный массив, количество элементов в котором равно количеству процессов в коммуникаторе. Элемент i задает смещение относительно начала буфера, в который принимается сообщение от i -го процесса.
  • Операция приведения, результат которой передается одному процессу

    int MPI_Reduce(void *buf, void *result, int count, 
    	MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)
    
    MPI_REDUCE(BUF, RESULT, COUNT, DATATYPE, OP, ROOT, COMM, IERR)

    Входные параметры:

  • buf - адрес буфера передачи;
  • count - количество элементов в буфере передачи;
  • datatype - тип данных в буфере передачи;
  • op - операция приведения;
  • root - ранг главного процесса;
  • comm - коммуникатор.
  • MPI_Reduce применяет операцию приведения к операндам из buf, а результат каждой операции помещается в буфер результата result (рис. 3.9). MPI_Reduce должна вызываться всеми процессами в коммуникаторе comm, а аргументы count, datatype и op в этих вызовах должны совпадать.

    (рис 3.9) Распределение данных при выполнении операции приведения

    Предопределенные операции приведения приведены в табл. 3.3.

    Определение собственных глобальных операций

    int MPI_Op_create(MPI_User_function *function, int commute, MPI_Op *op)
    MPI_OP_CREATE(FUNCTION, COMMUTE, OP, IERR)

    Входные параметры:

  • function - пользовательская функция;
  • commute - флаг, которому присваивается значение "истина", если операция коммутативна (результат не зависит от порядка операндов).
  • Описание типа пользовательской функции выглядит следующим образом:

    typedef void (MPI_User_function)(void *a, void *b, int *len, MPI_Datatype *dtype)

    Здесь операция определяется так:

    b[I] = a[I] op b[I] 
    для I = 0,  ...,   len-1.
    Предопределенные операции приведения MPI
    Операция Описание
    MPI_MAX Определение максимальных значений элементов одномерных массивов целого или вещественного типа
    MPI_MIN Определение минимальных значений элементов одномерных массивов целого или вещественного типа
    MPI_SUM Вычисление суммы элементов одномерных массивов целого, вещественного или комплексного типа
    MPI_PROD Вычисление поэлементного произведения одномерных массивов целого, вещественного или комплексного типа
    MPI_LAND Логическое "И"
    MPI_BAND Битовое "И"
    MPI_LOR Логическое "ИЛИ"
    MPI_BOR Битовое "ИЛИ"
    MPI_LXOR Логическое исключающее "ИЛИ"
    MPI_BXOR Битовое исключающее "ИЛИ"
    MPI_MAXLOC Максимальные значения элементов одномерных массивов и их индексы
    MPI_MINLOC Минимальные значения элементов одномерных массивов и их индексы

    Удаление пользовательской функции

    int MPI_Op_free(MPI_Op *op)
    MPI_OP_FREE(OP, IERR)

    После завершения вызова op присваивается значение MPI_OP_NULL.

    Одновременные сбор и распределение данных

    int MPI_Reduce_scatter(void *sendbuf, void *rcvbuf, int *rcvcounts, 
    MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
    MPI_REDUCE_SCATTER(SENDBUF,RCVBUF,RCVCOUNTS,DATATYPE,OP,COMM, IERR)

    Входные параметры:

  • sendbuf - стартовый адрес буфера приема;
  • rcvcounts - целочисленный одномерный массив, который задает количество элементов в результирующем массиве, распределяемом каждому процессу. Этот массив должен быть одинаковым во всех процессах, вызывающих данную подпрограмму;
  • datatype - тип данных в буфере приема;
  • op - операция;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - стартовый адрес буфера приема.
  • Каждая задача получает не весь результирующий массив, а его часть.

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

    int MPI_Allreduce(void *sendbuf, void *rcvbuf,  int count, 
    MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
    
    MPI_ALLREDUCE(SENDBUF, RCVBUF, COUNT, DATATYPE, OP, COMM, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • count - количество элементов в буфере передачи;
  • datatype - тип передаваемых данных;
  • op - операция приведения;
  • comm - коммуникатор. Выходной параметр:
  • rcvbuf - стартовый адрес буфера приема.
  • При аварийном завершении подпрограмма может возвращать код ошибки MPI_ERR_OP (некорректная операция). Это происходит, если применяется операция, которая не является предопределенной и которая не создана предшествующим вызовом подпрограммы MPI_Op_create.

    Операции сканирования (частичной редукции)

    int MPI_Scan(void *sendbuf,  void *rcvbuf,  int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
    
    MPI_SCAN(SENDBUF, RCVBUF, COUNT, DATATYPE, OP, COMM, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • count - количество элементов во входном буфере;
  • datatype - тип данных во входном буфере;
  • op - операция;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - стартовый адрес буфера приема.
  • Управление коммуникаторами

    Стандартный коммуникатор MPI_COMM_WORLD создается автоматически при запуске параллельной программы на выполнение. Напомним, что имеются также стандартные коммуникаторы:

  • MPI_COMM_SELF - коммуникатор, содержащий только вызывающий процесс;
  • MPI_COMM_NULL - пустой коммуникатор.
  • Получение доступа к группе group, связанной с коммуникатором comm

    int MPI_Comm_group(MPI_Comm comm, MPI_Group *group)
    
    MPI_COMM_GROUP(COMM, GROUP, IERR)

    Выходной параметр - группа. Для выполнения операций с группой к ней сначала необходимо получить доступ.

    Создание новой группы newgroup из n процессов, входящих в группу oldgroup

    int MPI_Group_incl(MPI_Group oldgroup,  int n,  int *ranks, MPI_Group *newgroup)
    
    MPI_GROUP_INCL(OLDGROUP,  N,   RANKS,  NEWGROUP,   IERR)

    Ранги процессов содержатся в массиве ranks. В новую группу войдут процессы с рангами ranks[0], ranks[n - 1],причем рангу i в новой группе соответствует ранг ranks[i] в старой группе. При n = 0 создается пустая группа MPI_GROUP_EMPTY. С помощью данной подпрограммы можно не только создать новую группу, но и изменить порядок процессов в старой группе.

    Создание группы newgroup исключением из исходной группы (group) процессы с рангами ranks[0] ..., ranks[n - 1]

    int MPI_Group_excl(MPI_Group oldgroup,  int n,  int *ranks, MPI_Group *newgroup)
    MPI_GROUP_EXCL(OLDGROUP, N, RANKS, NEWGROUP, IERR)

    При n = 0 новая группа тождественна старой.

    Создание группы newgroup из группы group добавлением в нее n процессов, ранг которых указан в массиве ranks

    int MPI_Group_range_incl(MPI_Group oldgroup,  int n,  int ranks[][3], MPI_Group *newgroup)
    
    MPI_GROUP_RANGE_INCL(OLDGROUP, N, RANKS, NEWGROUP, IERR)

    Массив ranks состоит из целочисленных триплетов вида (первый_1, последний_1, шаг_1), ..., (первый_n, последний_n, шаг_n). В новую группу войдут процессы с рангами (по первой группе) первый_1, первый_1 + шаг_1, ....

    Создание группы newgroup из группы group исключением из нее n процессов, ранг которых указан в массиве ranks

    int MPI_Group_range_excl(MPI_Group group,  int n,  int ranks[][3], MPI_Group *newgroup)
    
    MPI_GROUP_RANGE_EXCL(GROUP, N, RANKS, NEWGROUP, IERR)

    Массив ranks устроен так же, как аналогичный массив в подпрограмме MPI_Group_range_incl.

    Создание новой группы (newgroup) из разности двух групп (group1) и (group2)

    int MPI_Group_difference(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup)
    
    MPI_GROUP_DIFFERENCE(GROUP1, GROUP2, NEWGROUP, IERR)

    Создание новой группы (newgroup) из пересечения групп group1 и group2

    int MPI_Group_intersection(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup)
    
    MPI_GROUP_INTERSECTION(GROUP1, GROUP2, NEWGROUP, IERR)

    Создание группы (newgroup) объединением групп group1 и group2

    Int MPI_Group_union(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup)
    
    MPI_GROUP_UNION(GROUP1, GROUP2, NEWGROUP, IERR)

    Имеются и другие подпрограммы-конструкторы новых групп.

    Уничтожение группы group

    int MPI_Group_free(MPI_Group *group)
    
    MPI_GROUP_FREE(GROUP, IERR)

    Определение количества процессов (size) в группе (group)

    MPI_GROUP_SIZE(GROUP,   SIZE,   IERR)
    int MPI_Group_size(MPI_Group group,  int *size)

    Определение ранга (rank) процесса в группе group

    int MPI_Group_rank(MPI_Group group, int *rank)
    
    MPI_GROUP_RANK(GROUP, RANK, IERR)

    Если процесс не входит в указанную группу, возвращается значение MPI_UNDEFINED.

    Преобразование ранга процесса в одной группе в его ранг относительно другой группы

    int MPI_Group_translate_ranks(MPI_Group group1, int n, 
    	int *ranks1, MPI_Group group2, int *ranks2)
    MPI_GROUP_TRANSLATE_RANKS(GROUP1, N, RANKS1, GROUP2, RANKS2, IERR)

    Сравнение групп group1 и group2

    int MPI_Group_compare(MPI_Group group1, MPI_Group group2,  int *result)
    
    MPI_GROUP_COMPARE(GROUP1, GROUP2, RESULT, IERR)

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

    Создание дубликата уже существующего коммуникатора oldcomm int

    MPI_Comm_dup(MPI_Comm oldcomm, MPI_Comm *newcomm)
    
    MPI_COMM_DUP(OLDCOMM, NEWCOMM, IERR)

    В результате вызова данной подпрограммы создается новый коммуникатор ( newcomm ) с той же группой процессов, с теми же атрибутами, но с другим контекстом. Подпрограмма может применяться как к интра-, так и к интеркоммуникаторам.

    Создание нового коммуникатора (newcomm) из подмножества процессов (group) другого коммуникатора (oldcomm)

    int MPI_Comm_create(MPI_Comm oldcomm, MPI_Group group, MPI_Comm *newcomm)
    
    MPI_COMM_CREATE(OLDCOMM, GROUP, NEWCOMM, IERR)

    Вызов этой подпрограммы должны выполнить все процессы из старого коммуникатора, даже если они не входят в группу group, с одинаковыми аргументами. Если одновременно создаются несколько коммуникаторов, они должны создаваться в одной последовательности всеми процессами.

    Создание нескольких коммуникаторов сразу методом расщепления

    int MPI_Comm_split(MPI_Comm oldcomm,  int split,  int rank, MPI_Comm* newcomm)
    MPI_COMM_SPLIT(OLDCOMM, SPLIT, RANK, NEWCOMM, IERR)

    Группа процессов, связанных с коммуникатором oldcomm, разбивается на непересекающиеся подгруппы, по одной для каждого значения аргумента split. Процессы с одинаковым значением split образуют новую группу. Ранг в новой группе определяется значением rank. Если процессы A и B вызывают MPI_Comm_split с одинаковым значением split, а аргумент rank, переданный процессом A, меньше, чем аргумент, переданный процессом B, ранг A в группе, соответствующей новому коммуникатору, будет меньше ранга процесса B. Если же в вызовах используется одинаковое значение rank, система присвоит ранги произвольно. Для каждой подгруппы создается собственный коммуникатор newcomm.

    MPI_Comm_split должны вызвать все процессы из старого коммуникатора, даже если они не войдут в новый коммуникатор. Для этого в качестве аргумента split в подпрограмму передается предопределенная константа MPI_UNDEFINED. Соответствующие процессы вернут в качестве нового коммуникатора значение MPI_COMM_NULL. Новые коммуникаторы, созданные подпрограммой MPI_Comm_split, не пересекаются, однако с помощью повторных вызовов подпрограммы MPI_Comm_split можно создавать и перекрывающиеся коммуникаторы.

    Пометить коммуникатор comm для удаления

    int MPI_Comm_free(MPI_Comm *comm)
    
    MPI_COMM_FREE(COMM, IERR)

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

    Сравнение двух коммуникаторов (comm1) и (comm2)

    int MPI_Comm_compare(MPI_Comm comm1, MPI_Comm comm2,  int *result)
    
    MPI_COMM_COMPARE(COMM1, COMM2, RESULT, IERR)

    Выходной параметр:

  • result - целое значение, которое равно MPI_IDENT, если контексты и группы коммуникаторов совпадают; MPI_CONGRUENT, если совпадают только группы; MPI_SIMILAR и MPI_UNEQUAL, если не совпадают ни группы, ни контексты.
  • В качестве аргументов нельзя использовать пустой коммуникатор MPI_COMM_NULL.

    Присвоение коммуникатору comm строкового имени name

    int MPI_Comm_set_name(MPI_Comm com,  char *name) 
    MPI_COMM_SET_NAME(COM, NAME, IERR)

    Определение имени коммуникатора

    int MPI_Comm_get_name(MPI_Comm comm, char *name, int *reslen)
    MPI_COMM_GET_NAME(COMM, NAME, RESLEN, IERR)

    Выходной параметр:

  • name - строковое имя коммуникатора comm.;
  • reslen - длина имени.
  • Имя представляет собой массив символьных значений, длина которого должна быть не более MPI_MAX_NAME_STRING.

    Проверка, является ли коммуникатор comm (входной параметр) интеркоммуникатором

    int MPI_Comm_test_inter(MPI_Comm comm,  int *flag)
    
    MPI_COMM_TEST_INTER(COMM, FLAG, IERR)

    Выходной параметр:

  • flag - значение флага "истина", если аргументом является интеркоммуникатор.
  • Создание интракоммуникатора newcomm из интеркоммуникатора oldcomm

    int MPI_Intercomm_merge(MPI_Comm oldcomm,  int high, MPI_Comm *newcomm)
    
    MPI_INTERCOMM_MERGE(OLDCOMM, HIGH, NEWCOMM, IERR)

    Параметр high используется для упорядочения групп обоих интракоммуникаторов в comm при создании нового коммуникатора.

    Получение доступа к удаленной группе, связанной с интеркоммуникатором comm int

    MPI_Comm_remote_group(MPI_Comm comm, MPI_Group *group)
    
    MPI_COMM_REMOTE_GROUP(COMM, GROUP, IERR)

    Выходной параметр:

  • group - удаленная группа.
  • Определение размера удаленной группы, связанной с интеркоммуникатором comm int

    MPI_Comm_remote_size(MPI_Comm comm,  int *size)
    
    MPI_COMM_REMOTE_SIZE(COMM, SIZE, IERR)

    Выходной параметр:

  • size - количество процессов в области взаимодействия, связанной с коммуникатором comm.
  • Создание интеркоммуникатора

    int MPI_Intercomm_create(MPI_Comm local_comm,  int local_leader, 
    	MPI_Comm peer_comm, int remote_leader, int tag, MPI_Comm *new_intercomm)
    MPI_INTERCOMM_CREATE(LOCAL_COMM, LOCAL_LEADER, PEER_COMM, REMOTE_LEADER, TAG, NEW_INTERCOMM, IERR)

    Входные параметры:

  • local_comm - локальный интракоммуникатор;
  • local_leader - ранг лидера в локальном коммуникаторе (обычно 0);
  • peer_comm - удаленный коммуникатор;
  • remote_leader - ранг лидера в удаленном коммуникаторе (обычно 0);
  • tag - тег интеркоммуникатора, используемый лидерами обеих групп для обменов в контексте родительского коммуникатора.
  • Выходной параметр:

  • new_intercomm - интеркоммуникатор.
  • "Джокеры" в качестве параметров использовать нельзя. Вызов этой подпрограммы должен выполняться в обеих группах процессов, которые должны быть связаны между собой. В каждом из этих вызовов используется локальный интракоммуникатор, соответствующий данной группе процессов. При работе с MPI_Intercomm_create локальная и удаленная группы процессов не должны пересекаться, иначе возможны "тупики".

    Топологии

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

    Создание нового коммуникатора comm_cart посредством наделения декартовой топологией исходный коммуникатор comm_old (рис. 3.10)

    int MPI_Cart_create(MPI_Comm comm_old,  int ndims,  int *dims,  
    	int *periods,  int reorder, MPI_Comm *comm_cart)
    MPI_CART_CREATE(COMM_OLD, NDIMS, DIMS, PERIODS, REORDER, COMM_CART, IERR)

    Входные параметры:

  • comm_old - исходный коммуникатор;
  • ndims - размерность декартовой решетки;
  • dims -целочисленный массив, состоящий из ndims элементов, задающий количество процессов в каждом измерении;
  • periods - логический массив из ndims элементов, который определяет, является ли решетка периодической (значение true ) вдоль каждого измерения;
  • reorder - при значении этого параметра "истина", системе разрешено менять порядок нумерации процессов.
  • Информация о структуре декартовой топологии содержится в параметрах ndims, dims и periods. MPI_Cart_create является коллективной операцией (эту подпрограмму должны вызывать все процессы из коммуникатора, наделяемого декартовой топологией).

    (рис 3.10) Декартова топология

    Определение декартовых координат процесса по его рангу в группе

    int MPI_Cart_coords(MPI_Comm comm,  int rank,  int maxdims,  int *coords)
    
    MPI_CART_COORDS(COMM, RANK, MAXDIMS, COORDS, IERR)

    Входные параметры:

  • comm - коммуникатор, наделенный декартовой топологией;
  • rank - ранг процесса в comm ;
  • maxdims - длина вектора coords в вызывающей программе.
  • Выходной параметр:

  • coords - одномерный целочисленный массив (его размер равен ndims ), содержащий декартовы координаты процесса.
  • Определение ранга процесса (rank) по его декартовым координатам в коммуникаторе comm int

    MPI_Cart_rank(MPI_Comm comm, int *coords, int *rank)
    
    MPI_CART_RANK(COMM, COORDS, RANK, IERR)

    Входной параметр:

  • coords - целочисленный массив размера ndims, задающий декартовы координаты процесса. Как MPI_Cart_rank, так и MPI_Cart_coords локальны.
  • Расщепление коммуникатора comm на подгруппы, соответствующие декартовым подрешеткам меньшей размерности

    int MPI_Cart_sub(MPI_Comm comm, int *remain_dims, MPI_Comm *comm_new)
    MPI_CART_SUB(COMM, REMAIN_DIMS, COMM_NEW, IERR)

    I -й элемент массива remain_dims определяет, содержится ли I-е измерение в подрешетке ("истина"). Выходной параметр:

  • newcomm - коммуникатор, содержащий подрешетку, которой принадлежит данный процесс.
  • Подпрограмма MPI_Cart_sub используется только с коммуникатором, наделенным декартовой топологией.

    Получение информации о декартовой топологии, связанной с коммуникатором comm

    int MPI_Cart_get(MPI_Comm comm, int maxdims, int *dims, int *periods, int *coords)
    
    MPI_CART_GET(COMM, MAXDIMS, DIMS, PERIODS, COORDS, IERR)

    Входной параметр:

  • maxdims - задает длину массивов dims, periods и vectors в вызывающей программе
  • Выходные параметры:

  • dims - целочисленный массив, задающий количество процессов для каждого измерения;
  • periods - массив логических значений, задающих периодичность ( true, если решетка периодическая) для каждого измерения;
  • coords - целочисленный массив, задающий декартовы координаты вызывающего подпрограмму процесса.
  • Определение ранга процесса (newrank) в декартовой топологии после переупорядочения процессов

    int MPI_Cart_map(MPI_Comm comm_old, int ndims, int *dims, int *periods, int *newrank)
    MPI_CART_MAP(COMM_OLD, NDIMS, DIMS, PERIODS, NEWRANK, IERR)

    Входные параметры:

  • comm - коммуникатор;
  • ndims - размерность декартовой решетки;
  • dims - целочисленный массив, состоящий из ndims элементов, который определяет количество процессов вдоль каждого измерения;
  • periods - логический массив размера ndims, определяющий периодичность решетки вдоль каждого измерения.
  • Если процесс не принадлежит решетке, подпрограмма возвращает значение MPI_UNDEFINED.

    Определение рангов источника (source) сообщения, которое должно быть принято, и адресата (dest), который должен получить сообщение для заданного направления сдвига (direction) и его величины (disp)

    int MPI_Cart_shift(MPI_Comm comm, int direction, int displ, int *source,  int *dest)
    MPI_CART_SHIFT(COMM, DIRECTION, DISPL, SOURCE, DEST, IERR)

    Для n -мерной декартовой решетки значение аргумента direction должно находиться в пределах от 0 до n-1.

    Определение размерности (ndims) декартовой топологии, связанной с коммуникатором

    int MPI_Cartdim_get(MPI_Comm comm,  int *ndims) MPI_CARTDIM_GET(COMM, NDIMS, IERR)

    Создание нового коммуникатора comm_graph, наделенного топологией графа (рис. 3.11)

    int MPI_Graph_create(MPI_Comm comm,  int nnodes,  int *index, 
    	int *edges,  int reorder, MPI_Comm *comm_graph)
    
    MPI_GRAPH_CREATE(COMM, NNODES, INDEX, EDGES, REORDER, COMM_GRAPH, IERR)

    Входные параметры:

  • comm - исходный коммуникатор, не наделенный топологией;
  • nnodes - количество вершин графа;
  • index - целочисленный одномерный массив, содержащий порядок каждого узла (количество связанных с ним ребер);
  • edges - целочисленный одномерный массив, описывающий ребра графа;
  • reorder - значение "истина" разрешает изменение порядка нумерации процессов.
  • (рис 3.11) Виртуальная топология графа

    Определение вершин графа, соседних с данной

    int MPI_Graph_neighbors(MPI_Comm comm, int rank, int maxneighbors, int *neighbors)
    MPI_GRAPH_NEIGHBORS(COMM,  RANK,  MAXNEIGHBORS,  NEIGHBORS,   IERR)

    Входные параметры:

  • comm - коммуникатор с топологией графа;
  • rank - ранг процесса в группе коммуникатора comm ;
  • maxneighbors - размер массива neighbors.
  • Выходной параметр:

  • neighbors - массив, содержащий ранги процессов, соседних с данным.
  • Определение количества соседей (nneighbors) узла, связанного с топологией графа

    int MPI_Graph_neighbors_count(MPI_Comm comm,  int rank,  int *nneighbors)
    
    MPI_GRAPH_NEIGHBORS_COUNT(COMM, RANK, NNEIGHBORS, IERR)

    Входные параметры:

  • comm - коммуникатор;
  • rank - ранг процесса-узла.
  • Получение информации о топологии графа, связанной с коммуникатором comm

    int MPI_Graph_get(MPI_Comm comm, int maxindex, int maxedges, int *index,  int *edges)
    
    MPI_GRAPH_GET(COMM, MAXINDEX, MAXEDGES, INDEX, EDGES, IERR)

    Входные параметры:

  • comm - коммуникатор;
  • maxindex - длина массива index в вызывающей программе;
  • maxedges - длина массива edges в вызывающей программе.
  • Выходные параметры:

  • index - целочисленный массив, содержащий структуру графа (см. описание подпрограммы MPI_Graph_create );
  • edges - целочисленный массив, содержащий сведения о ребрах графа.
  • Определение ранга процесса в топологии графа после переупорядочения (newrank)

    int MPI_Graph_map(MPI_Comm comm,  int nnodes,  int *index,  int *edges, int *newrank)
    MPI_GRAPH_MAP(COMM, NNODES, INDEX, EDGES, NEWRANK, IERR)

    Входные параметры:

  • comm - коммуникатор;
  • nnodes - количество вершин графа;
  • index - целочисленный массив, задающий структуру графа (см. описание подпрограммы MPI_Graph_create );
  • edges - целочисленный массив, задающий ребра графа.
  • Если процесс не принадлежит графу, подпрограмма возвращает значение MPI_UNDEFINED.

    Получение информации о топологии графа, связанной с коммуникатором comm int

    MPI_Graphdims_get(MPI_Comm comm, int *nnodes, int *nedges)
    
    MPI_GRAPHDIMS_GET(COMM, NNODES, NEDGES, IERR)

    Выходные параметры:

  • nnodes - количество вершин графа;
  • nedges - количество ребер графа.
  • Определение типа топологии (toptype), связанной с коммуникатором comm int

    MPI_Topo_test(MPI_Comm comm,  int *toptype)
    
    MPI_TOPO_TEST(COMM, TOPTYPE, IERR)

    Выходной параметр:

  • toptype - тип топологии (значения MPI_CART для декартовой топологии и MPI_GRAPH для топологии графа).
  • Производные типы

    Производные типы данных MPI используются для пересылки данных, элементы которых располагаются в памяти не подряд, а с "пробелами". Производный тип сначала создается с помощью вызова подпрограммы-конструктора, а потом регистрируется. Перед завершением программы производный тип должен быть удален.

    Конструктор векторного типа

    int MPI_Type_vector(int count,  int blocklen,  int stride, 
    	MPI_Datatype oldtype, MPI_Datatype *newtype)
    MPI_TYPE_VECTOR(COUNT, BLOCKLEN, STRIDE, OLDTYPE, NEWTYPE, IERR)

    Входные параметры:

  • count - количество блоков (неотрицательное целое значение);
  • blocklen - длина каждого блока (количество элементов, неотрицательное целое);
  • stride - количество элементов, расположенных между началом предыдущего и началом следующего блока ("гребенка");
  • oldtype - базовый тип.
  • Выходной параметр:

  • newtype - идентификатор нового типа, который назначается программистом.
  • Исходные данные однотипные.

    Конструктор векторного типа (рис. 3.12)

    int MPI_Type_hvector(int count, int blocklen, MPI_Aint stride, 
    	MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_HVECTOR(COUNT, BLOCKLEN, STRIDE, OLDTYPE, NEWTYPE, IERR)

    Смысл и назначение параметров этой подпрограммы совпадают с подпрограммой MPI_Type_vector, только значение параметра stride задается в байтах.

    (рис 3.12) Векторный производный тип

    Конструктор структурного типа

    int MPI_Type_struct(int count,  int blocklengths[], MPI_Aint indices[], 
    	MPI_Datatype oldtypes[], MPI_Datatype *newtype)
    
    MPI_TYPE_STRUCT(COUNT, BLOCKLENGTHS, INDICES, OLDTYPES, NEWTYPE, IERR)

    Входные параметры:

  • count - задает количество элементов в производном типе, а также длину массивов
  • oldtypes, indices и blocklengths ;
  • blocklengths - количество элементов в каждом блоке (массив);
  • indices - смещение каждого блока в байтах (массив);
  • oldtypes - тип элементов в каждом блоке (массив).
  • Выходной параметр:

  • newtype - идентификатор производного типа.
  • MPI_Aint - представляет собой скалярный тип, длина которого имеет размер, одинаковый с указателем.

    Конструктор индексированного типа

    int MPI_Type_indexed(int count,  int blocklens[],  int indices[], 
    	MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_INDEXED(COUNT, BLOCKLENS, INDICES, OLDTYPE, NEWTYPE, IERR)

    Входные параметры:

  • count - количество блоков, одновременно длина массивов indices и blocklens ;
  • blocklens - количество элементов в каждом блоке;
  • indices - смещение каждого блока, которое задается в количестве ячеек базового типа (целочисленный массив);
  • oldtype - базовый тип. Выходной параметр:
  • newtype - идентификатор производного типа.
  • Конструктор индексированного типа

    int MPI_Type_hindexed(int count, int blocklens[], MPI_Aint indices[], 
    	MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_HINDEXED(COUNT, BLOCKLENS, INDICES, OLDTYPE, NEWTYPE, IERR)

    Смещения indices задаются в байтах:

    Конструктор типа данных с непрерывным расположением элементов

    int MPI_Type_contiguous(int count, MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_CONTIGUOUS(COUNT, OLDTYPE, NEWTYPE, IERR)

    Входные параметры:

  • count - счетчик повторений;
  • oldtype - базовый тип
  • Выходной параметр:

  • newtype - идентификатор нового типа.
  • Конструктор индексированного типа с блоками постоянного размера

    int MPI_Type_create_indexed_block(int count, int blocklength,  
    	int displacements[], MPI_Datatype oldtype, MPI_Datatype *newtype)
    MPI_TYPE_CREATE_INDEXED_BLOCK(COUNT, BLOCKLENGTH, DISPLACEMENTS, OLDTYPE,   NEWTYPE, IERR)

    Входные параметры:

  • count - количество блоков и размер массивов indices и blocklens ;
  • blocklength - количество элементов в каждом блоке;
  • displacements - смещение каждого блока в единицах длины типа oldtype (целочисленный массив);
  • oldtype - базовый тип.
  • Выходной параметр:

  • newtype - идентификатор производного типа.
  • Конструктор типа данных, соответствующего подмассиву многомерного массива

    int MPI_Type_create_subarray(int ndims, int *sizes, int *subsizes, 
    	int *starts, int order, MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_CREATE_SUBARRAY(NDIMS, SIZES, SUBSIZES, STARTS, ORDER, OLDTYPE, NEWTYPE, IERR)

    Входные параметры:

  • ndims - размерность массива;
  • sizes - количество элементов типа oldtype в каждом измерении полного массива;
  • subsizes - количество элементов типа oldtype в каждом измерении подмассива;
  • starts - стартовые координаты подмассива в каждом измерении;
  • order - флаг, задающий переупорядочение;
  • oldtype - базовый тип.
  • Выходной параметр:

  • newtype - новый тип.
  • Регистрация производного типа datatype, сконструированного программистом

    int MPI_Type_commit(MPI_Datatype *datatype)
    
    MPI_TYPE_COMMIT(DATATYPE, IERR)

    Удаление производного типа datatype

    int MPI_Type_free(MPI_Datatype *datatype)
    
    MPI_TYPE_FREE(DATATYPE,   IERR)

    Базовые типы данных не могут быть удалены.

    Определение размера типа datatype в байтах (объем памяти, занимаемый одним элементом данного типа)

    int MPI_Type_size(MPI_Datatype datatype,  int *size)
    
    MPI_TYPE_SIZE(DATATYPE, SIZE, IERR)

    Выходной параметр - размер size.

    Определение количества элементов данных в одном объекте типа datatype (его экстент)

    int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint *extent)
    
    MPI_TYPE_EXTENT(DATATYPE, EXTENT, IERR)

    Выходной параметр - extent.

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

    Определение адреса (address) по заданному положению (location)

    int MPI_Address(void *location, MPI_Aint *address)
    
    MPI_ADDRESS(LOCATION, ADDRESS, IERR)

    Может использоваться в программах на языках C и FORTRAN. В C она обычно возвращает тот же адрес, что и оператор , хотя иногда это не так. Данная подпрограмма может понадобиться в программе на языке FORTRAN, в C есть собственные средства для определения адреса.

    Определение фактических параметров, использованных при создании производного типа

    int MPI_Type_get_contents(MPI_Datatype datatype,  int max_integers,  
    	int max_addresses,  int max_datatypes,  int *integers, MPI_Aint *addresses, MPI_Datatype *datatypes)
    
    MPI_TYPE_GET_CONTENTS(DATATYPE, MAX_INTEGERS, MAX_ADDRESSES,
    MAX_DATATYPES, INTEGERS, ADDRESSES, DATATYPES,   IERR)

    Входные параметры:

  • datatype - идентификатор типа;
  • max_integers - количество элементов в массиве integers ;
  • max_addresses - количество элементов в массиве addresses ;
  • max_datatypes - количество элементов в массиве datatypes.
  • Выходные параметры:

  • integers - содержит целочисленные аргументы, использованные при конструировании указанного типа;
  • addresses - содержит аргументы address, использованные при конструировании указанного типа;
  • datatypes - содержит аргументы datatype, использованные при конструировании указанного типа.
  • Определение нижней границы типа данных datatype

    int MPI_Type_lb(MPI_Datatype datatype, MPI_Aint Misplacement)
    
    MPI_TYPE_LB(DATATYPE, DISPLACEMENT, IERR)

    Выходной параметр:

  • displacement - смещение (в байтах) нижней границы относительно источника.
  • Определение верхней границы типа

    int MPI_Type_ub(MPI_Datatype datatype, MPI_Aint Misplacement)
    MPI_TYPE_UB(DATATYPE, DISPLACEMENT, IERR)

    Упаковка данных

    int MPI_Pack(void *inbuf, int incount, MPI_Datatype datatype, void *outbuf,  
    	int outcount,  int *position, MPI_Comm comm)
    
    MPI_PACK(INBUF, INCOUNT, DATATYPE, OUTBUF, OUTCOUNT, POSITION, COMM, IERR)

    При вызове incount элементов указанного типа выбираются из входного буфера и упаковываются в выходном буфере, начиная с положения position.

    Входные параметры:

  • inbuf - начальный адрес входного буфера;
  • incount - количество входных данных;
  • datatype - тип каждого входного элемента данных;
  • outcount - размер выходного буфера в байтах;
  • position - текущее положение в буфере в байтах;
  • comm - коммуникатор для упакованного сообщения.
  • Выходной параметр:

  • outbuf - стартовый адрес выходного буфера.
  • Распаковка данных

    int MPI_Unpack(void *inbuf, int insize, int *position, void *outbuf, 
    	int outcount, MPI_Datatype datatype, MPI_Comm comm)
    
    MPI_UNPACK(INBUF, INSIZE, POSITION, OUTBUF, OUTCOUNT, DATATYPE, COMM,
    IERR)

    Входные параметры:

  • inbuf - стартовый адрес входного буфера;
  • insize - размер входного буфера в байтах;
  • position - текущее положение в байтах;
  • outcount - количество данных, которые должны быть распакованы;
  • datatype - тип каждого выходного элемента данных;
  • comm - коммуникатор для упаковываемого сообщения.
  • Выходной параметр:

  • outbuf - стартовый адрес выходного буфера.
  • Определение объема памяти size (в байтах), необходимого для распаковки сообщения

    int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size)
    MPI_PACK_SIZE(INCOUNT, DATATYPE, COMM, SIZE, IERR)

    Входные параметры:

  • incount - аргумент count, использованный при упаковке;
  • datatype - тип упакованных данных;
  • comm - коммуникатор.
  • Атрибуты

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

    Создание нового ключа атрибута keyval (выходной параметр)

    int MPI_Keyval_create(MPI_Copy_function *copy_fn, MPI_Delete_function *delete_fn, 
    	int *keyval, void *extra_state)
    
    MPI_KEYVAL_CREATE(COPY_FN,DELETE_FN, KEYVAL, EXTRA_STATE, IERR)

    Ключи уникальны для каждого процесса и не видны пользователю, хотя явным образом хранятся в виде целых значений. Будучи однажды задан, ключ может быть использован для задания атрибутов и доступа к ним в любом коммуникаторе. Функция copy_fn вызывается, когда коммуникатор дублируется подпрограммой MPI_Comm_dup, а функция delete_fn используется для удаления. Параметр extra_state задает дополнительную информацию (состояние) для функций копирования и удаления.

    Задание типа функции MPI_Copy_function

    typedef int MPI_Copy_function(MPI_Comm oldcomm,  int keyval,  
    	void *extra_state, void *attribute_val_in, void *attribute_val_out,  int *flag)
    
    SUBROUTINE COPY_FUNCTION(OLDCOMM, KEYVAL, EXTRA_STATE,
    ATTTRIBUTE_VAL_IN, ATTRIBUTE_VAL_OUT, FLAG, IERR)

    Функция копирования вызывается для каждого значения ключа в исходном коммуникаторе в произвольном порядке. Каждое обращение к функции копирования выполняется со значением ключа и соответствующим ему атрибутом. Если она возвращает значение флага flag = 0, атрибут удаляется из продублированного коммуникатора. В противном случае ( flag = 1 ) устанавливается новое значение атрибута, равное значению, возвращенному в параметре attribute_val_out.

    Функцию copy_fn в языках C или FORTRAN можно определить значениями MPI_NULL_COPY_FN или MPI_DUP_FN. Значение MPI_NULL_COPY_FN является функцией, которая не выполняет никаких действий, только возвращает значение флага flag = 0 и MPI_SUCCESS. Значение MPI_DUP_FN представляет собой простейшую функцию дублирования. Она возвращает значение флага flag = 1, значение атрибута в переменной attribute_val_out и код завершения MPI_SUCCESS.

    Аналогичной copy_fn является функция удаления, которая определяется следующим образом. Функция delete_fn вызывается, когда коммуникатор удаляется вызовом MPI_Comm_free или при вызове MPI_Attr_delete. Она должна иметь тип MPI_Delete_function, который определяется следующим образом:

    typedef int MPI_Delete_function(MPI_Comm comm,  int keyval, void *attribute_val, void *extra_state);
     SUBROUTINE DELETE_FUNCTION(COMM, KEYVAL, ATTRIBUTE VAL, EXTRA STATE, IERR)

    Эта функция вызывается подпрограммами MPI_Comm_free, MPI_Attr_delete и MPI_Attr_put. Функция удаления может быть "пустой" - MPI_NULL_DELETE_FN.

    Функция MPI_NULL_DELETE_FN не выполняет никаких действий, возвращая только значение MPI_SUCCESS.

    Специальное значение ключа MPI_KEYVAL_INVALID никогда не возвращается подпрограммой MPI_Keyval_create. Его можно использовать для инициализации ключей.

    Удаление ключа keyval

    int MPI_Keyval_free(int *keyval)
    
    MPI_KEYVAL_FREE(KEYVAL, IERR)

    Эта функция присваивает параметру keyval значение MPI_KEYVAL_INVALID. Используемый атрибут можно удалить, поскольку фактическое удаление происходит только после того, как будут удалены все ссылки на атрибут. Эти ссылки должны быть явным образом удалены программой, например, посредством вызова MPI_Attr_delete - каждый такой вызов удаляет один экземпляр атрибута, либо вызовом MPI_Comm_free, который удаляет все экземпляры атрибута, связанные с удаляемым коммуникатором.

    Задание атрибута attribute, который в дальнейшем может использоваться подпрограммой MPI_Attr_get

    int MPI_Attr_put(MPI_Comm comm, int keyval,  void* attribute)
    
    MPI_ATTR_PUT(COMM, KEYVAL, ATTRIBUTE, IERR)

    С атрибутом ассоциируется значение ключа keyval. Если значение атрибута уже задано, результат будет аналогичен ситуации, когда сначала для удаления предыдущего значения вызывается MPI_Attr_delete (и выполняется функция обратного вызова delete_fn ), а затем сохраняется новое значение. Вызов завершится с ошибкой, если нет ключа со значением keyval. В частности, MPI_KEYVAL_INVALID - ошибочное значение ключа. Не допускается изменение системных атрибутов MPI_TAG_UB, MPI_HOST, MPI_IO и MPI_WTIME_IS_GLOBAL. Если атрибут уже назначен, вызывается функция удаления, заданная при создании соответствующего ключа.

    Определение значения атрибута attribute, соответствующее значению ключа keyval

    int MPI_Attr_get(MPI_Comm comm, int keyval, void *attribute,  int *flag)
    
    MPI_ATTR_GET(COMM, KEYVAL, ATTRIBUTE, FLAG, IERR)

    Первый параметр задает коммуникатор, с которым связан атрибут. Если ключа со значением keyval нет, возникает ошибка. Ошибки не возникает, если значение key существует, но соответствующий атрибут не присоединен к коммуникатору comm. В этом случае возвращается значение флага flag = false.

    Вызов MPI_Attr_put передает в параметре attribute_val значение атрибута, а вызов подпрограммы MPI_Attr_get передает в параметре attribute_val адрес, по которому возвращается значение атрибута. Атрибуты должны извлекаться из программ, написанных на тех же языках, на которых они задавались с помощью вызова подпрограммы MPI_Attr_put.

    Удаление атрибута с указанным значением ключа

    int MPI_Attr_delete(MPI_Comm comm,  int keyval)
    
    MPI_ATTR_DELETE(COMM, KEYVAL, IERR)

    Делается это с помощью функции удаления атрибута delete_fn, заданной при создании keyval. Параметр comm задает коммуникатор, с которым связан атрибут. Все аргументы данной подпрограммы входные. При любом дублировании коммуникатора с помощью подпрограммы MPI_Comm_dup вызываются все функции копирования для атрибутов, установленных в данный момент времени. Порядок вызова произволен. Аналогичные действия выполняются при удалении коммуникатора вызовом MPI_Comm_free, но вызываются все функции удаления.

    Реализации MPI

    Существуют различные реализации MPI. Среди них MPICH ( MPI CHameleon, www.mcs.anl.gov) - свободно распространяемая реализация MPI с открытым кодом; LAM ( Local Area Multicomputer ) - еще одна реализация MPI (www.lam-mpi.org); Microsoft $$\text{\textregistered}$$ MPI и Intel $$\text{\textregistered}$$ MPI и т. д.

    Существуют реализации MPI, ориентированные на работу в среде грид.

    Новое в спецификации MPI-2

    В спецификации MPI-2 появились новые возможности, превратившие MPI в ещё более гибкий инструмент разработки параллельных программ.

    Вот краткий перечень новых (по сравнению с MPI-1 ) возможностей:

  • возможность запуска новых процессов во время выполнения MPI- программы;
  • односторонние двухточечные обмены;
  • параллельные операции ввода-вывода;
  • модифицированные привязки к языкам;
  • новые предопределённые типы данных;
  • расширенные возможности коллективных обменов;
  • внешние интерфейсы;
  • поддержка многопоточности и другие.
  • Возможность запуска новых процессов во время выполнения MPI-программы

    В MPI-1 параллельная программа запускается в определённом и фиксированном количестве процессов. Это не позволяет приложению, например, "подстраиваться" под изменяющуюся трудоёмкость расчёта. В то же время такой инструмент параллельного программирования как PVM поддерживает возможность динамического изменения числа процессов параллельного приложения. Эта возможность появилась в MPI-2.

    Дополнительный процесс (несколько процессов) может быть запущен во время выполнения программы. Процесс может быть также остановлен.

    Запуск одного процесса

    int MPI_Comm_spawn(char *command,  char *argv[], int maxprocs, MPI_Info info,  
      int root, MPI_Comm comm, MPI_Comm *intercomm,  int array_of_errcodes[])
    MPI_Comm_spawn(command, argv, maxprocs, info, root, comm, intercomm, array_of_errcodes,  ierror)

    Входные параметры:

  • command - командная строка запуска процесса;
  • argv - аргументы командной строки запуска процесса;
  • maxprocs - максимальное количество запускаемых процессов;
  • info - указывает системе как и где запускается процесс;
  • root - ранг главного процесса.
  • Выходные параметры:

  • intercomm - интеркоммуникатор между исходной группой процессов и вновь запущенными процессами;
  • array_of_errcodes - коды завершения для запущенных процессов.
  • При запуске группы процессов для них создаётся собственный коммуникатор MPI_COMM_WORLD, отличный от такого же для родительских процессов. Это коллективная операция. Она завершается после того, как во всех дочерних процессах состоится вызов MPI_Init. Завершение данного вызова в родительском процессе не означает, что в дочерних процессах завершены все вызовы MPI_Init.

    Интеркоммуникатор intercomm содержит родительский процесс в локальной группе и дочерние процессы.

    Командная строка запуска дочернего процесса представляет собой строку, содержащую имя исполняемого файла.

    Дочерний процесс обязательно должен вызывать MPI_Init, иначе результат не определён.

    Аргумент argv является массивом строковых значений, представляющих аргументы запускаемых программ.

    При вызове MPI_Comm_spawn предпринимается попытка запустить maxprocs процессов. Если какой-то процесс не может быть запущен, возвращается значение MPI_ERR_SPAWN.

    Аргумент info создаётся вызовом специальной подпрограммы MPI_Info_create.

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

    Опуская подробное описание её параметров, приведём интерфейс:

    Запуск нескольких процессов

    int MPI_Comm_spawn_multiple(int count, char *array_of_commands[], char **array_of_argv[], 
      int array_of_maxprocs[], MPI_Info array_of_info[], int root, 
      MPI_Comm comm, MPI_Comm *intercomm, int array_of_errcodes[])
    
    MPI Comm_spawn_multiple(count, array_of_commands, array_of_argv, 
      array_of_ maxprocs, array_of_info, root, comm, intercomm, array_of_errcodes, ierror)

    Подпрограмма MPI_Get_parent возвращает родительский интеркоммуникатор вызывающего процесса:

    Определение родительского интеркоммуникатора

    int MPI_Comm_get_parent(MPI_Comm *parent)
    
    MPI_Comm_get_parent(parent, ierr)

    Если данный процесс не является дочерним по отношению к какому-либо другому процессу, возвращается значение "пустого" коммуникатора MPI_COMM_NULL.

    Взаимодействие между группами процессов

    MPI-2 допускает организацию обмена сообщениями между группами процессов, которые запущены независимо друг от друга. Это позволяет, например, "подключиться" к параллельной программе приложению, выполняющему обработку данных. Данная возможность полезна при создании клиент-серверных приложений и в других ситуациях.

    Основные механизмы взаимодействия:

  • связь по имени;
  • связь через порт.
  • Программная реализация взаимодействия - в функциях:

  • MPI_Open_port
  • MPI_Close_port
  • MPI_Comm_accept
  • MPI_Publish_name
  • MPI_Unpublish_name
  • MPI_Lookup_name и других подпрограммах.
  • Односторонние обмены

    Односторонние обмены основаны на механизме удалённого доступа к памяти ( RMA -Remote Memory Access ) и позволяют процессу, инициировавшему обмен, самостоятельно задать параметры обмена как для источника, так и для адресата сообщения.

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

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

    В односторонних обменах эти функции разделены.

    Односторонний обмен возможен, если процесс создаёт "окно", доступное всем остальным процессам (рис. 3.13).

    Окно создаётся (коллективным) вызовом подпрограммы MPI_Win_create.

    Создание окна

    int MPI_Win_create(void *base, MPI_Aint size, int disp_unit, 
    MPI_Info info, MPI_Comm comm, MPI_Win *win)
    
    MPI_Win_create(base, size, disp unit, info, comm, win, ierror)

    Входные параметры:

  • base - адрес окна;
  • size - размер окна в байтах;
  • disp_unit - масштабный множитель для вычисления смещений;
  • info - информационный параметр;
  • comm - коммуникатор.
  • Выходной параметр - win - окно.

    (рис 3.13) Окно одностороннего обмена

    Аннулировать окно можно вызовом подпрограммы:

    int MPI_Win_free(MPI_Win *win) MPI_Win_free(win, ierror)

    Три операции одностороннего обмена являются неблокирующими операциями:

  • MPI_Put - передача данных от отправителя в окно;
  • MPI_Get - передача данных из окна отправителю;
  • MPI_Accumulate - обновление окна получателя.
  • Операция Put

    int MPI_Put(void *origin_addr,  int origin_count, MPI_Datatype origin_datatype,  
      int target_rank, MPI_Aint target_disp,  int target_count, 
      MPI_Datatype target_datatype, MPI_Win win)
    MPI_Put(origin_addr,  origin_count,  origin_datatype,  target_rank, 
    	target_disp, target_count, target_datatype, win,  ierror)

    Входные параметры:

  • origin_addr - адрес буфера отправки сообщения;
  • origin_count - количество элементов в буфере отправки;
  • origin_datatype - тип передаваемых данных;
  • target_rank - ранг адресата;
  • target_disp - смещение от начала окна приёма до буфера приёма;
  • target_count - количество принимаемых данных;
  • target_datatype - тип принимаемых данных;
  • win - окно приёма.
  • При выполнении этой операции данные размещаются в буфере приёма по адресу

    адрес_окна + смещение х disp_unit

    При вызове подпрограммы MPI_Get данные копируются в обратном направлении - из памяти адресата в память "источника".

    Коллективные операции

    В MPI-2 расширены возможности коллективных обменов сообщениями.

    Расширения заключаются в обобщении некоторых операций коллективного обмена на интеркоммуникаторы, введении дополнительных конструкторов интеркоммуникаторов, введении двух новых операций обмена - обобщённой операции "all-to-all" и операции исключающего сканирования. Есть и другие расширения.

    Подпрограмма MPI_Comm_create может использоваться для создания интеркоммуникаторов.

    Внешние интерфейсы

    Механизм внешних интерфейсов позволяет программисту добавить новую функциональность поверх базовой функциональности MPI.

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

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

    При использовании обобщённых запросов "ответственность" за выполнение операции берёт на себя приложение. Оно сообщает MPI о завершении операции.

    Операции с обобщёнными запросами:

  • MPI_Grequest_start
  • MPI_Grequest_complete
  • и некоторые другие.

    Другие возможности MPI-2

    Среди других возможностей MPI-2:

  • Операции декодирования производных типов.
  • Ассоциирование пользовательской информации с полями структуры status.
  • Присвоение имён объектам MPI (например, коммуникаторам, окнам и др.).
  • Многопоточность ( MPI_Init_thread, MPI_Thread_single, MPI_Thread_multiple и др.).
  • Новые операции кеширования атрибутов.
  • Параллельные операции ввода-вывода ( MPI_File_open, MPI_File_close, MPI_File_read, MPI_File_write и др.).
  • Лабораторная работа 0.1 Знакомство со структурой MPI-программы и процедурами блокирующего двухточечного обмена MPI

    Двухточечные блокирующие обмены

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

    Трансляция MPI-программ

    Утилиты трансляции и сборки находятся в каталоге /каталог_установки_МРI/bin.Его следует включить в путь поиска исполняемых файлов. Утилиты трансляции запускают трансляторы обычных языков C/C ++ и Fortran. В командную строку их запуска, при необходимости, подставляют ссылки на необходимые библиотечные и заголовочные файлы.

    Для трансляции и компоновки программ на языке C++ используется команда mpiCC, для трансляции программ на языке C - команда mpicc. Для трансляции и компоновки программ на языке Fortran 77 и Fortran 90 используются команды mpif77 и mpif90.

    Информацию о ключах можно найти на справочных страницах MPICH (для их просмотра применяют команду man ). Пример применения команды:

    mpicc -o fft fft.c

    Назначить другой транслятор можно, определив значения переменных окружения MPICH_CC, MPICH_F77, MPICH_CCC или MPICH_F90. Изменить программу-компоновщик можно с помощью переменных окружения MPICH_CLINKER, MPICH_F77LINKER, MPICH_CCLINKER и MPICH_F9 0LINKER.

    Выполнение MPI-программ

    Для выполнения MPI -программ в MPICH используется загрузчик приложений mpirun. Он запускает указанное количество копий программы. Команда запуска:

    mpirun -np n [ключи MPI] программа[ключи и аргументы программы]

    где n - число запускаемых процессов. Некоторые ключи MPI (они указываются перед именем исполняемого файла программы) приведены в таблице:

    Ключ Описание
    - h Краткая информация о команде
    - machinefile файл Для запуска программы использовать список компьютеров из указанного файла

    Лабораторная работа

    В заданиях лабораторной работы 0.1 предлагается дописать или исправить предлагаемые фрагменты программ на языках Fortran и C, написанные с использованием процедур MPICH 1.2.7, в том числе, процедуры блокирующего двухточечного обмена. Пропущенные фрагменты обозначены многоточием.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 27 - 29 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

    В исходном тексте программы на языке Fortran пропущены вызовы процедур подключения к MPI, определения количества процессов и ранга процесса. Добавить эти вызовы, откомпилировать и запустить программу.

    Program main_mpi include 'mpif.h' 
    integer myid, numprocs, ierr
    …
    print *, "
    process", myid, "of", numprocs
    call mpi_finalize(ierr)
    stop
    end

    Задание 2

    В исходном тексте программы на языке C пропущены вызовы процедур подключения к MPI, определения количества процессов и ранга процесса. Добавить эти вызовы, откомпилировать и запустить программу.

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myid,  numprocs;
    
      fprintf(stdout,"Process %d of %d\n",  myid,  numprocs);
      MPI_Finalize();
      return 0;
    }

    Задание 3

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myid,  numprocs; 
      char message[20]; 
      int myrank; 
      MPI_Status status; 
      int TAG = 0; 
    MPI_Init(argc, argv);
    MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
    if   (myrank == 0)
    {
      strcpy(message, "Hi, Second Processor!"); 
      MPI_Send(...);
    }
      else
    {
      MPI_Recv(...);
      printf("received:  %s\n",  message);
    }
      MPI_Finalize(); 
      return 0;
    }

    Задание 4

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myrank,  size,  message; int TAG = 0; 
      MPI_Status status; 
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank);
      MPI_Comm_size(MPI_COMM_WORLD, size);
      message = myrank;
      if((myrank % 2) == 0)
    {
      if((myrank + 1) != size) MPI_Send(...);
    }
      else
    {
      if(myrank  != 0) MPI_Recv(...);
      printf("received :%i\n",  message);
    }
      MPI_Finalize(); return 0;
    }

    Задание 5

    Следующая программа на языке Fortran написана некорректно. Найдите ошибку и исправьте ее.

    program main_mpi include  'mpif.h'
    integer rank,  tag,  cnt,  ierr,  status(mpi_status_size) 
    real sndbuf,  rcvbuf tag = 0
    sndbuf = 3.14159 cnt = 1
    call mpi_init(ierr)
    call mpi_comm_rank(mpi_comm_world, rank, ierr) 
    if (rank.eq.0)   then
    call mpi_recv(rcvbuf, cnt, mpi_real, 1, tag, mpi_comm_world, status, ierr)
    call mpi_send(sndbuf, cnt, mpi_real, 1, tag, mpi_comm_world, ierr)
    else
    call mpi_recv(rcvbuf, cnt, mpi_real, 0, tag, mpi_comm_world, status, ierr)
    call mpi_send(sndbuf, cnt, mpi_real, 0, tag, mpi_comm_world, ierr)
    end if
    call mpi_finalize(ierr)
    stop
    end

    Лабораторная работа 0.2 Знакомство с процедурами буферизованного и неблокирующегодвухточечного обмена MPI

    Двухточечный буферизованный обмен

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

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

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

    Двухточечные неблокирующие обмены

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

    Неблокирующий обмен выполняется в два этапа:

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

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

    Подпрограмма MPI_Wait блокирует работу процесса до завершения приема или передачи сообщения. Функции MPI_Wait и MPI_Test можно использовать для завершения операций приема и передачи.

    Подпрограммы-пробники

    Получить информацию о сообщении до его помещения в буфер приема можно с помощью подпрограмм-пробников MPI_Probe и MPI_IProbe. На основании полученной информации принимается решение о дальнейших действиях. С помощью вызова подпрограммы MPI_Probe фиксируется поступление (но не прием!) сообщения. Затем определяется источник сообщения, его длина, выделяется буфер подходящего размера и выполняется прием сообщения.

    Лабораторная работа

    В заданиях лабораторной работы 0.2 предлагается дописать предлагаемые фрагменты программ на языках Fortran и C, написанные с использованием процедур MPICH 1.2.7. Пропущенные фрагменты обозначены многоточием.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 30 - 34 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int *buffer; 
      int myrank; 
      MPI_Status status; 
      int buffsize = 1; 
      int TAG = 0; 
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
      if (myrank == 0)
    {
      buffer = (int *) malloc(buffsize + MPI_BSEND_OVERHEAD); 
      buffer = (int *) 10;
    }
      else
    {
      MPI_Recv(buffer,  buffsize,  MPI_INT, 0, TAG, MPI_COMM_WORLD,
    status); 
      printf("received:  %i\n",  buffer);
    }
      MPI_Finalize(); return 0;
    }

    Задание 2

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

    program main_mpi include  'mpif.h'
    integer rank, tag, cnt, ierr, status(mpi_status_size) integer request
    real sndbuf(5) /1., 2., 3., 4., 5./ real rcvbuf(5) 
    cnt = 5 
    tag = 0
    call mpi_init(ierr)
    call mpi_comm_rank(mpi_comm_world, rank, ierr) if(rank.eq.0) then
    ...
    print  *, "process  ", rank, " send before wait", sndbuf
    ...
    print  *, "process  ", rank, " send after wait", sndbuf else
    ...
    print  *, "process  ", rank, " received before wait", rcvbuf
    ...
    print  *, "process  ", rank, " received after wait", rcvbuf 
    end if
    call mpi_finalize(ierr)
    stop
    end

    Задание 3

    В исходном тексте программы на языке C пропущены вызовы подпрограмм-пробников. Добавить эти вызовы, откомпилировать и запустить программу.

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myid, numprocs, **buf, source, i;
      int message[3]  = {0, 1, 2};
      int myrank, data = 2002, count, TAG = 0;
      MPI_Status status;
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank);
      if(myrank == 0)
    {
      MPI_Send(data, 1, MPI_INT, 2, TAG, MPI_COMM_WORLD);
    }
      else if (myrank == 1)  
    {
      MPI_Send(message, 3, MPI_INT, 2, TAG, MPI_COMM_WORLD);
    }
      else
    {
    ...
    source = status.MPI_SOURCE;
    MPI_Get_count (...) ;
    for (i = 0;  i < count;	
    {
      buf[i]  =  (int *)malloc(count*sizeof(int));
    }
      MPI_Recv(buf[0], count, MPI_INT, source, TAG, MPI_COMM_WORLD, status);
      for  (i = 0;  i < count;  i++)
       { printf("received:  %d\n",  buf[i]);
       }
    }
    MPI_Finalize(); 
    return 0;
    }

    Задание 4

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

    program main_mpi include  'mpif.h'
    integer rank, i, k, ierr, tag,dest,... 
    real x 
    tag = 0 
    dest = 2
    call mpi_init(ierr)
    call mpi_comm_rank(mpi_comm_world,  rank,   ierr) if (rank.eq.0)   
      then i = 2002
    call mpi_send(i, 1, mpi_integer, dest, tag, mpi_comm_world, ierr)
    else if(rank.eq.l)   then 
    x = 3.14159
    call mpi_send(x, 1, mpi_real, dest, tag, mpi_comm_world, ierr) 
    else
    do k = 1, 2
    ...
      if   (status(mpi_source).eq.0)   then
     call mpi_recv(i, 1, mpi_integer, 0, tag, mpi_comm_world, status,  ierr)
      print *, "received ", i, "  from 0" else
    call mpi_recv(x, 1, mpi_real, 1, tag, mpi_comm_world, status, ierr)
      print *, "received ", x, "  from 1"
      end if 
    end do 
    end if
    call mpi_finalize(ierr) 
    end

    Задание 5

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

    program main_mpi
    
    integer rank, tagl, tag2, cnt, ierr, ...
    integer request
    real sndbuf1, sndbuf2, rcvbuf1, rcvbuf2
    cnt = l tag = 0
    sndbuf1 = 3.14159 sndbuf2 = 2.71828
    ...
    call mpi_comm_rank(mpi_comm_world,  rank,   ierr) if (rank.eq.0) then
    call mpi_ssend(sndbuf1, cnt, mpi_real, 1, tag1, mpi_comm_world, ierr)
    print *, "process ", rank, " send ",  sndbuf1
    call mpi_send(sndbuf2, cnt, mpi_real, 1, tag2, mpi_comm_world, ierr)
    print *, "process ", rank, " send ",   sndbuf2 
    else
    call mpi_irecv(rcvbuf1,cnt, mpi_real, 0, tag1, mpi_comm_world, request, ierr)
    call mpi_recv(rcvbuf2, cnt, mpi_real, 0, tag2, mpi_comm_world, status,   ierr)
    print *, "process ", rank, " received before wait", rcvbuf1 
    print *, "process ", rank, " received before wait",  rcvbuf2
    ...
    print *,"process ", rank, " received after wait", rcvbuf1 
    print *, "process ", rank, " received after wait", rcvbuf2 
    end if
    ...
    end

    Лабораторная работа 0.3 Знакомство с процедурами коллективного обмена MPI

    Коллективные обмены

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

    Коллективные обмены характеризуются следующим:

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

    Широковещательная рассылка

    Широковещательная рассылка выполняется выделенным процессом, который называется главным ( root ). Все остальные процессы, принимающие участие в обмене, получают по одной копии сообщения от главного процесса (рис. 6.1).

    (рис 6.1) Широковещательная рассылка

    Выполняется широковещательная рассылка с помощью подпрограммы MPI_Bcast (см. Методическое пособие).

    Операции редукции

    Операции редукции относятся к категории глобальных вычислений. В глобальной операции приведения к данным от всех процессов из заданного коммуникатора применяется операция MPI_Reduce (рис. 6.2).

    (рис 6.2) Глобальная операция приведения

    Аргументом операции приведения является массив данных - по одному элементу от каждого процесса. Результат такой операции - единственное значение.

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

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

    Коммуникаторы бывают двух типов: интракоммуникаторы - для операций внутри одной группы процессов и интеркоммуникаторы - для двухточечного обмена между двумя группами процессов.

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

    Созданию нового коммуникатора предшествует создание соответствующей группы процессов. Операции создания групп аналогичны математическим операциям над множествами:

  • объединение - к процессам первой группы добавляются процессы второй группы, не принадлежащие первой;
  • пересечение - в новую группу включаются все процессы, принадлежащие двум группам одновременно. Ранги им назначаются как в первой группе;
  • разность - в новую группу включаются все процессы первой группы, не входящие во вторую группу. Ранги назначаются как в первой группе.
  • Новую группу можно создать только из уже существующих групп. Базовая группа, из которой формируются все другие группы, связана с коммуникатором MPI_COMM_WORLD.

    Доступ к группе group, связанной с коммуникатором comm можно получить, обратившись к подпрограмме MPI_Comm_group.

    В MPI имеются подпрограммы-конструкторы новых групп (см. Методическое пособие). Есть и деструктор - MPI_Group_free.

    Создание коммуникатора - коллективная операция и соответствующая подпрограмма должна вызываться всеми процессами коммуникатора. Подпрограмма MPI_Comm_dup дублирует уже существующий коммуникатор.

    Подпрограмма MPI_Comm_create создает новый коммуникатор из подмножества процессов другого коммуникатора.

    Вызов этой подпрограммы должны выполнить все процессы из старого коммуникатора, даже если они не входят в группу group, с одинаковыми аргументами. Данная операция применяется только к интракоммуникаторам. Она позволяет выделять подмножества процессов со своими областями взаимодействия, если, например, требуется уменьшить "зернистость" параллельной программы.

    Лабораторная работа

    В заданиях лабораторной работы 0.3 предлагается дописать предлагаемые фрагменты программ на языке C, написанные с использованием процедур MPICH 1.2.7. Пропущенные фрагменты обозначены многоточием.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 37 - 49 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      char data[24]; 
      int myrank, count = 25; 
      MPI_Status status; 
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
      if (myrank == 0)
    {
      strcpy(data, "Hi, Parallel Programmer!"); 
      ...
      printf("send: %s\n", data);
    }
      else
    {
    ...
    printf("received: %s\n", data);
    }
    MPI_Finalize(); return 0;
    }

    Задание 2

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc, char *argv[])
    {
      int myrank; 
      int root = 0; int count = 1; float a, b; 
      int n;
    
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
      if (myrank == 0)
    {
      printf("Enter a, b, n\n");
      scanf("%f %f %i", a, b, n);
    …
    }
      else
    {
    …
      printf("%i Process got %f %f %i\n", myrank, a, b, n);
    }
      MPI_Finalize(); return 0;
    }

    Задание 3

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      char message[24];
      MPI_Group MPI_GROUP_WORLD;
      MPI_Group group; 
      MPI_Comm fcomm; 
      int size, q, proc; 
      int* process_ranks; 
      int rank, rank_in_group;
    
      MPI_Status status; 
      MPI_Init(argc, argv);
      MPI_Comm_size(MPI_COMM_WORLD, size); 
      MPI_Comm_rank(MPI_COMM_WORLD, rank);
      printf("New group contains processes:"); 
      q = size - 1;
      process_ranks = (int*) malloc(q*sizeof(int)); 
      for (proc = 0; proc < q; proc++)
    {
      process_ranks[proc]   = proc; 
      printf("%i ", process_ranks[proc]);
    }
      printf("\n");
    ...
      if (fcomm != MPI_COMM_NULL)  
    {
      MPI_Comm_group(group, fcomm); 
      MPI_Comm_rank(fcomm, rank_in_group); 
      if (rank_in_group == 0)   
    {
      strcpy(message, "Hi, Parallel Programmer!"); 
      MPI_Bcast(message, 25, MPI_BYTE, 0, fcomm); 
      printf("0 send: %s\n", message);
    }
      else
    {
      MPI_Bcast(message, 25, MPI_BYTE, 0, fcomm); 
      printf("%i received: %s\n", rank_in_group, message);
    }
      MPI_Comm_free(fcomm); 
      MPI_Group_free(group);
    }
      MPI_Finalize(); return 0;
    }

    Задание 4

    В программе на языке C сначала создается подгруппа, состоящая из процессов с рангами 1, 3, 5 и 7, и соответствующий ей коммуникатор. Затем выполняется редукция (суммирование) по процессам, входящим в новую группу. Вызов подпрограммы редукции и некоторые другие важные фрагменты пропущены. Добавить эти вызовы, откомпилировать и запустить программу.

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myrank, i;
      int count = 5,  root = 1;
      MPI_Group MPI_GROUP_WORLD, subgroup;
      int ranks[4] = {1, 3, 5, 7};
      MPI_Comm subcomm;
      int sendbuf[5] = {1, 2, 3, 4, 5}; 
      int recvbuf[5];
    
      MPI_Init(argc, argv);
      MPI_Comm_group(MPI_COMM_WORLD, MPI_GROUP_WORLD); 
      MPI_Group_incl(MPI_GROUP_WORLD, 4, ranks, subgroup); 
      MPI_Group_rank(subgroup, myrank);
      ...
      if(myrank != MPI_UNDEFINED)
    {
      MPI_Reduce(sendbuf, recvbuf, count, MPI_INT, MPI_SUM, root, subcomm);
    
      if(myrank == root)   
        { printf("Reduced values"); 
          for(i = 0;  i < count;  i++)
       { printf(" %i  ", recvbuf[i]);}
        }
      printf("\n");
    
      MPI_Comm_free(subcomm);
      MPI_Group_free(MPI_GROUP_WORLD);
      ...
    }
      MPI_Finalize(); return 0;
    }

    Лабораторная работа 0.4 Производные типы в MPI

    Производные типы

    Производные типы данных создаются во время выполнения программы. Создание типа - двухступенчатый процесс, который состоит из двух шагов:

  • конструирование типа;
  • регистрация типа.
  • После завершения работы с производным типом, он аннулируется. При этом все производные от него типы остаются и могут использоваться дальше, пока и они не будут уничтожены. Последовательность удаления может быть любой.

    Производные типы данных создаются из базовых типов с помощью подпрограмм-конструкторов. Операции создания производных типов могут применяться рекурсивно.

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

    Подпрограмма MPI_Type_struct (см. Методическое пособие) является наиболее общим конструктором типа в MPI - программист может использовать полное описание каждого элемента типа. Если пересылаемые данные содержат подмножество элементов массива, такая детальная информация не нужна, поскольку у всех элементов один и тот же базовый тип. MPI содержит три конструктора, которые можно использовать в такой ситуации: MPI_Type_contiguous, MPI_Type_vector и MPI_Type_indexed. Первый из них создает производный тип, элементы которого являются непрерывно расположенными элементами массива. Второй создает тип, элементы которого расположены на одинаковых расстояниях друг от друга, а третий создает тип, содержащий произвольные элементы.

    "Векторный" тип создается конструктором MPI_Type_vector. Схема расположения данных в новом типе представлена на рис. 6.3.

    (рис 6.3) Схема векторного типа данных

    Лабораторная работа

    В заданиях лабораторной работы 0.4 предлагается дописать предлагаемые фрагменты программ на языке C, написанные с использованием процедур MPICH 1.2.7. Пропущенные фрагменты обозначены многоточием.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 55 - 60 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

    В программе на языке Fortran имеется трехмерный массив arr. Используя приведенный ниже шаблон, дополните программу таким образом, чтобы в ней определялся производный тип, соответствующий: а) горизонтальному сечению массива; б) вертикальному сечению массива. Затем сечение должно пересылаться от процесса с рангом 0 процессу с рангом 1. Откомпилировать и запустить программу.

    program main_mpi
    include  'mpif.h'
    parameter  (n = 50)
    real arr(n, n, n), b(...)
    integer slice, sizeofreal
    integer rank, ierr, status(MPI_STATUS_SIZE)
    integer tag, cnt, vcount, blocklen, stride, count
    cnt = ...
    tag = 0
    vcount = ...
    blocklen = ...
    call MPI_Init(ierr)
    call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr) 
    if (rank.eq.0)   then
    call MPI_Type_extent(MPI_REAL, sizeofreal, ierr) stride = ...
    call MPI_Type_vector(...)
    call MPI_Type_commit(slice, ierr)
    call MPI_Send(arr (...),  cnt,  slice, 1, tag, MPI_COMM_WORLD, ierr) 
      else if (rank.eq.1)   
      then count = ...
    call MPI_Recv(b, count,  MPI_REAL, 0, tag, MPI_COMM_WORLD, status,  ierr) 
    print *,  b 
    end if
    call MPI_Finalize(ierr) end

    Задание 2

    В программе на языке C задаются типы членов производного типа, затем количество элементов каждого типа. После этого вычисляются адреса членов типа indata и определяются смещения трех членов производного типа относительно адреса первого, для которого смещение равно 0. Затем определяется производный тип. Аргументы подпрограмм MPI_Type_struct и MPI_Type_commit, а также некоторые другие фрагменты пропущены. Добавить эти фрагменты, откомпилировать и запустить программу.

    #include "mpi.h" 
    #include <stdio.h> 
    struct newtype  
    {
      Float a; float b; int n;
    };
    
    int main(int argc,char *argv[])
    {
      int myrank;
      MPI_Datatype NEW_MESSAGE_TYPE; 
      int block_lengths[3] ; 
      MPI_Aint displacements[3]; 
      MPI_Aint addresses[4]; 
      MPI_Datatype typelist[3]; 
      int blocks_number; 
      struct newtype indata; 
      int tag = 0; 
      MPI_Status status;
      ...
      MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
      typelist[0] = MPI_FLOAT;
      typelist[1] = MPI_FLOAT; 
      typelist[2] = MPI_INT;
      block_lengths[0] = block_lengths[1] = block_lengths[2] = 1; 
      MPI_Address(indata, addresses[0]); 
      MPI_Address((indata.a), addresses[1]); 
      MPI_Address((indata.b), addresses[2]); 
      MPI_Address((indata.n), addresses[3]); 
      displacements[0]  = addresses[1] - addresses[0]; 
      displacements[1]  = addresses[2] - addresses[0]; 
      displacements[2]  = addresses[3] - addresses[0]; 
      blocks_number = 3; 
      MPI_Type_struct(...); 
      MPI_Type_commit(...);
    
      if   (myrank == 0)
    {
      indata.a = 3.14159; 
      indata.b = 2.71828; 
      indata.n = 2002;
    
      MPI_Send(indata, 1,NEW_MESSAGE_TYPE, 1, tag, MPI_COMM_WORLD);
      printf("Process  %i  send:  %f  %f  %i\n", 
      myrank,  indata.a, indata.b, indata.n); 
    }
      else {
      MPI_Recv(indata, 1, NEW_MESSAGE_TYPE, 0, 
        tag, MPI_COMM_WORLD, status);
      printf("Process %i received: %f %f %i, status %s\n", 
        myrank, indata.a,  indata.b,  indata.n,  status.MPI_ERROR);
    }
      ...
      MPI_Finalize(); return 0;
    }

    Лабораторная работа 0.5 Виртуальные топологии в MPI

    Виртуальные топологии

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

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

    В MPI существуют два типа топологии:

  • декартова топология - прямоугольная решетка произвольной размерности (рис. 6.4);
  • топология графа.
  • Над топологиями можно выполнять различные операции. Декартовы решетки можно расщеплять на гиперплоскости, удаляя некоторые измерения. Данные можно сдвигать вдоль выбранного измерения декартовой решетки.

    Сдвигом называют пересылку данных между процессами вдоль определенного измерения. Вдоль избранного измерения могут быть организованы коллективные обмены.

    Для того, чтобы связать структуру декартовой решетки с коммуникатором MPl_COMM_WORLD, необходимо задать следующие параметры:

  • размерность решетки (значение 2 соответствует плоской, двумерной решетке);
  • размер решетки вдоль каждого измерения (размеры {10, 15}, например, соответствуют плоской прямоугольной решетке, протяженность которой вдоль оси x составляет 10 узлов-процессов, а вдоль оси y - 15 узлов);
  • периодичность вдоль каждого измерения (решетка может быть периодической, если процессы, находящиеся на противоположных концах ряда, взаимодействуют между собой).
  • MPI дает возможность системе оптимизировать отображение виртуальной топологии процессов на физическую с помощью изменения порядка нумерации процессов в группе.

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

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

    (рис 6.4) Декартова топология

    Обратным действием по отношению к MPI_Cart_coords обладает подпрограмма MPI_Cart_rank. С ее помощью можно определить ранг процесса по его декартовым координатам в соответствующем коммуникаторе.

    Между процессами, организованными в декартову решетку, могут выполняться обмены особого вида. Это сдвиги, о которых мы уже упоминали. Имеются два типа сдвигов данных по группе из N процессов:

  • циклический сдвиг на J позиций вдоль ребра решетки. Данные от процесса K пересылаются процессу с номером (J + K) mod N ;
  • линейный сдвиг на J позиций вдоль ребра решетки, когда данные в процессе K пересылаются процессу с номером J + K,если ранг адресата находится в пределах между 0 и N.
  • Лабораторная работа

    В заданиях лабораторной работы 0.5 предлагается дописать и модифицировать предлагаемый фрагмент программы на языке C, написанный с использованием процедур MPI.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 51 - 55 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      MPI_Comm grid_comm; int dims[2]; 
      int periodic[2];
      int reorder = 1, q = 5, ndims = 2, maxdims = 2; 
      int coordinates[2]; 
      int my_grid_rank; 
      int coords[2]; 
      MPI_Comm row_comm;
    
      dims[0] = dims[1] = q; 
      periodic[0] = periodic[1] = 1; 
      coords[0] = 0;  
      coords[1] = 1; 
      MPI_Init(argc,  argv);
    
      MPI_Cart_create(MPI_COMM_WORLD, ndims, dims, periodic, reorder, grid_comm);
    
      printf("Process rank %i has coordinates %i %i\n", 
        my_grid_rank, coordinates[0],  coordinates[1]);
    
      MPI_Finalize(); return 0;
    }

    Задание 2

    Добавьте в программу из Задания 1 операции циклического и/или линейного сдвига вдоль определенного измерения.

    Задание 3

    Измените программу из Задания 1, наделив коммуникатор топологией некоторого графа. Детали обсудите с преподавателем.

    Страницы:

    Далее приводится описание свободно распространяемой версии MPI - MPICH 1.2.7, соответствующей спецификации MPI 1.

    Модель параллельной программы в MPI

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

    Каждому процессу в области взаимодействия назначается уникальный числовой идентификатор - ранг,значение которого от 0 до np - 1 ( np - число процессов). Ранги, назначаемые одному и тому же процессу в разных коммуникаторах, вообще говоря, различны.

    Структура программы, написанной по схеме хозяин/работник, приведена ниже.

    program para
    ...
    if  (ранг процесса = рангу мастер-процесса)  then
    код мастер-процесса else
    код подчиненного процесса (подчиненных процессов)
    endif
    end

    Каждый экземпляр программы уже в процессе своего выполнения определяет, является ли он мастер-процессом. Затем, в зависимости от результата этой проверки, выполняется одна из ветвей условного оператора. Первая ветвь соответствует мастер-задаче, а вторая - подчиненной задаче. Способы взаимодействия между подзадачами определяются программистом.

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

    Сообщения

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

  • ранг процесса-отправителя сообщения;
  • адрес, по которому размещаются пересылаемые данные процесса-отправителя;
  • тип пересылаемых данных;
  • количество данных;
  • ранг процесса, который должен получить сообщение;
  • адрес, по которому должны быть размещены данные процессом-получателем.
  • тег сообщения;
  • идентификатор коммуникатора, описывающего область взаимодействия, внутри которой происходит обмен.
  • Тег - это задаваемое пользователем целое число от 0 до 32767, которое играет роль идентификатора сообщения и позволяет различать сообщения, приходящие от одного процесса. Теги могут использоваться и для соблюдения определенного порядка приема сообщений.

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

    Разновидности обменов сообщениями

    В MPI реализованы разные виды обменов. Прежде всего, это двухточечные (задействованы только два процесса) и коллективные (задействованы более двух процессов).

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

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

  • Привязка к языку Fortran

    Имена подпрограмм и констант MPI в программах на языке Fortran начинаются с MPI _. При вызове подпрограмм коды завершения передаются через дополнительный параметр целого типа (находится на последнем месте в списке параметров подпрограммы). Код успешного завершения - MPI_SUCCESS. Константы и другие объекты MPI описываются в файле mpif.h, который включается в MPI -программу с помощью оператора include.

    В некоторых подпрограммах используется переменная status, которая является массивом стандартного целого типа. Его размер MPI_STATUS_SIZE.

    При обращении к подпрограммам MPI используются типы данных MPI, для большинства из которых имеется соответствие базовым типам языка (см. табл. 3.1)

    Типы MPI_Datatype и MPI_Comm - эмулируются стандартным целым типом языка Fortran (Integer).

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

    Типы данных MPI для языка Fortran
    Тип данных MPI Тип данных Fortran
    MPI_INTEGER Integer
    MPI_REAL Real
    MPI_DOUBLE_PRECISION Double precision
    MPI_DOUBLE_COMPLEX Double complex
    MPI_COMPLEX Complex
    MPI_LOGICAL Logical
    MPI_CHARACTER Character
    MPI_BYTE Нет соответствия
    MPI_PACKED Нет соответствия
    Типы, которые имеются не во всех реализациях MPI
    MPI_INTEGER1 Integer*1
    MPI_INTEGER2 Integer*2
    MPI_INTEGER4 Integer*4
    MPI_REAL4 Real*4
    MPI_REAL8 Real*8

    Привязка к языку C

    В программах на языке C имена подпрограмм имеют вид Класс_действие_подмножество или Класс_действие. В C++ подпрограмма является методом для определенного класса, имя имеет в этом случае вид MPI::Класс::действие_подмножество. Для некоторых действий введены стандартные наименования: Create - создание нового объекта, Get - получение информации об объекте, Set - установка параметров объекта, Delete - удаление информации, Is - запрос о том, имеет ли объект указанное свойство.

    Имена констант MPI записываются в верхнем регистре. Их описания находятся в заголовочном файле mpi.h.

    Входные параметры функций передаются по значению, а выходные (и INOUT ) - по ссылке. Соответствие типов MPI стандартным типам языка C приведено в табл. 3.2.

    Коды завершения

    В MPI приняты стандартные соглашения о кодах завершения вызовов подпрограмм. Так, например, возвращаются значения MPI_SUCCESS - при успешном завершении вызова и MPI_ERR_OTHER - обычно при попытке повторного вызова процедуры MPI_Init.

    Вместо числовых кодов в программах обычно используют специальные именованные константы:

  • MPI_ERR_BUFFER - неправильный указатель на буфер;
  • MPI_ERR_COMM - неправильный коммуникатор;
  • MPI_ERR_RANK - неправильный ранг;
  • MPI_ERR_OP - неправильная операция;
  • MPI_ERR_ARG - неправильный аргумент;
  • MPI_ERR_UNKNOWN - неизвестная ошибка;
  • MPI_ERR_TRUNCATE - сообщение обрезано при приеме;
  • MPI_ERR_INTERN - внутренняя ошибка. Обычно возникает, если системе не хватает памяти.
  • Типы данных MPI для языка С
    Тип данных MPI Тип данных C
    MPI_CHAR Signed char
    MPI_SHORT Signed short int
    MPI_INT Signed int
    MPI_LONG Signed long int
    MPI_UN SIGNE D_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

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

  • MPI_COMM_WORLD - включает все процессы параллельной программы;
  • MPI_COMM_SELF - включает только данный процесс;
  • MPI_COMM_NULL - пустой коммуникатор, не содержит ни одного процесса.
  • В MPI имеются процедуры, позволяющие создавать новые коммуникаторы, содержащие подмножества процессов.

    (рис 3.1) Коммуникатор

    Ранг процесса представляет собой уникальный числовой идентификатор, назначаемый процессу в том или ином коммуникаторе. Ранги в разных коммуникаторах назначаются независимо и имеют целое значение от 0 до число_процессов - 1 (рис. 3.2).

    (рис 3.2) Ранги процессов

    Тег (маркер) сообщения - это уникальный числовой идентификатор, который назначается сообщению и позволяет различать сообщения, если в этом есть необходимость. Если тег не требуется, вместо него можно использовать "джокер" MPI_ANY_TAG.

    Типовая структура MPI -программы:

    program para
    ...
    if (process = master) then
    master clause else
    slave clause endif end

    Различные подпрограммы MPI

    Подключение к MPI

    int MPI_Init(int *argc, char **argv)
    
    MPI_INIT(IERR)

    Аргументы argc и argv требуются только в программах на C, где они задают количество аргументов командной строки запуска программы и вектор этих аргументов. Данный вызов предшествует всем прочим вызовам подпрограмм MPI.

    Завершение работы с MPI

    p>int MPI_Finalize()
    
    MPI_FINALIZE(IERR)

    После вызова данной подпрограммы нельзя вызывать подпрограммы MPI. MPI_FINALIZE должны вызывать все процессы перед завершением своей работы.

    Определение размера области взаимодействия

    p>int MPI_Comm_size(MPI_Comm comm, int *size)
    MPI_COMM_SIZE(COMM., SIZE, IERR)

    Входные параметры:

  • comm - коммуникатор.
  • Выходные параметры:

  • size - количество процессов в области взаимодействия.
  • Определение ранга процесса

    int MPI_Comm_rank(MPI_Comm comm, int *rank)
    MPI_COMM_RANK(COMM, RANK, IERR)

    Входные параметры:

  • comm- коммуникатор.
  • Выходные параметры:

  • rank - ранг процесса в области взаимодействия.
  • Определение имени узла, на котором выполняется данный процесс

    MPI_Get_processor_name(char *name, int *resultlen)
    
    MPI_GET_PROCESSOR_NAME(NAME, RESULTLEN, IERR)

    Выходные параметры:

  • name - идентификатор вычислительного узла. Массив не менее чем из MPI_MAX_PROCESSOR_NAME элементов;
  • resultlen - длина имени.
  • Время, прошедшее с произвольного момента в прошлом

    double MPI_Wtime()
    
    MPI_WTIME(TIME,   IERR)

    Двухточечный обмен

    Участниками двухточечного обмена являются два процесса: процесс-отправитель и процесс-получатель (рис. 3.3).

    (рис 3.3) Двухточечный обмен

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

    Стандартная блокирующая передача

    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, IERR)

    Входные параметры:

  • buf - адрес первого элемента в буфере передачи;
  • count - количество элементов в буфере передачи;
  • datatype - тип MPI каждого пересылаемого элемента;
  • dest - ранг процесса-получателя сообщения (целое число от 0 до n - 1, где n - число процессов в области взаимодействия);
  • tag - тег сообщения;
  • comm - коммуникатор;
  • ierr - код завершения.
  • Стандартный блокирующий прием

    int MPI_Recv(void *buf,  int count, MPI_Datatype datatype,  int source, 
    	int tag, MPI_Comm comm, MPI_Status *status)
    MPI_RECV(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, STATUS, IERR)

    Входные параметры:

  • count - максимальное количество элементов в буфере приема. Фактическое их количество можно определить с помощью подпрограммы MPI_Get_count ;
  • datatype - тип принимаемых данных. Напомним о необходимости соблюдения соответствия типов аргументов подпрограмм приема и передачи;
  • source - ранг источника. Можно использовать специальное значение MPI_ANY_SOURCE, соответствующее произвольному значению ранга. В программировании идентификатор, отвечающий произвольному значению параметра, часто называют "джокером". Этот термин будем использовать и мы;
  • tag - тег сообщения или "джокер" MPI_ANY_TAG, соответствующий произвольному значению тега;
  • comm - коммуникатор. При указании коммуникатора "джокеры" использовать нельзя.
  • Выходные параметры:

  • buf - начальный адрес буфера приема. Его размер должен быть достаточным, чтобы разместить принимаемое сообщение, иначе при выполнении приема произойдет сбой - возникнет ошибка переполнения;
  • status - статус обмена.
  • Если сообщение меньше, чем буфер приема, изменяется содержимое лишь тех ячеек памяти буфера, которые относятся к сообщению.

    Определение размера полученного сообщения (count)

    int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count)
    
    MPI_GET_COUNT(STATUS, DATATYPE, COUNT, IERR)

    Аргумент datatype должен соответствовать типу данных, указанному в операции передачи сообщения.

    Синхронная передача

    int MPI_Ssend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
    
    MPI_SSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERR)

    Параметры этой подпрограммы совпадают с параметрами подпрограммы MPI_Send.

    Буферизованный обмен

    int MPI_Bsend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
    
    MPI_BSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERR)

    Параметры совпадают с параметрами подпрограммы MPI_Send.

    Создание буфера

    int MPI_Buffer_attach(void *buf,  size)
    
    MPI_BUFFER_ATTACH(BUF, SIZE, IERR)

    Выходной параметр:

  • buf - буфер размером size байтов.
  • В программах на языке Fortran роль буфера может играть массив. Этот массив должен быть описан в программе, его не следует использовать для других целей (например, в качестве первого аргумента подпрограммы MPI_Bsend ). За один раз к процессу может быть подключен только один буфер.

    Отключение буфера

    int MPI_Buffer_detach(void *buf, int *size)
    MPI_BUFFER_DETACH(BUF, SIZE, IERR)

    Выходные параметры:

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

    Передача по готовности

    int MPI_Rsend(void *buf, int count, MPI_Datatype datatype, 
    	int dest, int tag,  MPI_Comm comm)
    
    MPI_RSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERR)

    Параметры совпадают с параметрами подпрограммы MPI_Send.

    Блокирующая проверка доставки сообщения

    int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status)
    
    MPI_PROBE(SOURCE, TAG, COMM, STATUS, IERR)

    Входные параметры:

  • source - ранг источника или " джокер";
  • tag - значение тега или "джокер";
  • comm - коммуникатор.
  • Выходной параметр:

  • status - статус.
  • Неблокирующая проверка сообщения

    int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag, MPI_Status *status)
    
    MPI_IPROBE(SOURCE, TAG, COMM, FLAG, STATUS, IERR)

    Входные параметры этой подпрограммы те же, что и у подпрограммы MPI_Probe

    Выходные параметры:

  • flag - флаг;
  • status - статус.
  • Если сообщение уже поступило и может быть принято, возвращается значение флага "истина".

    Прием и передача данных с блокировкой

    int MPI_Sendrecv(void *sendbuf,  int sendcount, MPI_Datatype sendtype, 
    	int dest,  int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype,  
    	int source,  int recvtag, MPI_Comm comm, MPI_Status *status)
    MPI_SENDRECV(SENDBUF, SENDCOUNT, SENDTYPE, DEST, SENDTAG, 
    	RECVBUF, RECVCOUNT, RECVTYPE, SOURCE, RECVTAG, COMM, STATUS, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • sendcount - количество передаваемых элементов;
  • sendtype - тип передаваемых элементов;
  • dest - ранг адресата;
  • sendtag - тег передаваемого сообщения;
  • recvbuf - начальный адрес буфера приема;
  • recvcount - количество элементов в буфере приема;
  • recvtype - тип элементов в буфере приема;
  • source - ранг источника;
  • recvtag - тег принимаемого сообщения;
  • comm - коммуникатор.
  • Выходные параметры:

  • recvbuf - начальный адрес буфера приема;
  • status - статус операции приема.
  • Прием, и передача используют один и тот же коммуникатор. Буферы передачи и приема не должны пересекаться, у них может быть разный размер, типы пересылаемых и принимаемых данных также могут различаться.

    Отправка и прием сообщения в блокирующем режиме с общим буфером для передачи и для приема

    int MPI_Sendrecv_replace(void *buf,  int count, MPI_Datatype datatype, 
    	int dest,  int sendtag,  int source,  int recvtag, MPI_Comm comm, MPI_Status *status)
    
    MPI_SENDRECV_REPLACE(BUF, COUNT, DATATYPE, DEST, SENDTAG, SOURCE, RECVTAG, COMM, STATUS, IERR)

    Входные параметры:

  • count - количество отправляемых данных и емкость буфера приема;
  • datatype - тип данных в буфере приема и передачи;
  • dest - ранг адресата;
  • sendtag - тег передаваемого сообщения;
  • source - ранг источника;
  • recvtag - тег принимаемого сообщения;
  • comm - коммуникатор.
  • Выходные параметры:

  • buf - начальный адрес буфера приема и передачи;
  • status - статус.
  • Принимаемое сообщение не должно превышать по размеру отправляемое сообщение, а передаваемые и принимаемые данные должны быть одного типа. Последовательность приема и передачи выбирается системой автоматически.

    Инициализация неблокирующей стандартной передачи

    int MPI_Isend(void *buf,  int count, MPI_Datatype datatype,  
    	int dest, int tag, MPI_Comm comm, MPI_Request *request)
    MPI_ISEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Входные параметры этой подпрограммы аналогичны аргументам подпрограммы MPI_Send.

    Выходной параметр:

  • request - идентификатор операции.
  • Инициализация неблокирующей синхронной передачи данных

    int MPI_Issend(void *buf, int count, MPI_Datatype datatype, 
    	int dest, int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_ISSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Параметры этой подпрограммы совпадают с параметрами подпрограммы MPI_Send.

    Неблокирующая буферизованная передача сообщения

    int MPI_Ibsend(void *buf, int count, MPI_Datatype datatype, int dest, 
    	int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_IBSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Неблокирующая передача "по готовности"

    int MPI_Irsend(void* buf, int count, MPI_Datatype datatype, int dest, 
    	int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_IRSEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Параметры всех подпрограмм неблокирующей передачи совпадают.

    Инициализация неблокирующего приема

    int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, 
    int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_IRECV(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, REQUEST, IERR)

    Назначение аргументов здесь такое же, как и в предыдущих подпрограммах, за исключением того, что указывается ранг не адресата, а источника сообщения ( source ).

    Блокировка работы процесса до завершения приема или передачи сообщения

    int MPI_Wait(MPI_Request *request, MPI_Status *status)
    MPI_WAIT(REQUEST, STATUS, IERR)

    Входной параметр:

  • request - идентификатор операции обмена.
  • Выходной параметр:

  • status - статус выполненной операции.
  • Значение статуса для операции передачи сообщения можно получить вызовом подпрограммы MPI_Test_cancelled. Можно вызвать MPI_Wait с пустым или неактивным аргументом request. В этом случае операция завершается сразу же с пустым статусом.

    Успешное выполнение подпрограммы MPI_Wait после вызова MPI_Ibsend подразумевает, что буфер передачи можно использовать вновь, то есть пересылаемые данные отправлены или скопированы в буфер, выделенный при вызове подпрограммы MPI_Buffer_attach. В этот момент уже нельзя отменить передачу. Если не будет зарегистрирован соответствующий прием, буфер нельзя будет освободить. В этом случае можно применить подпрограмму MPI_Cancel, которая освобождает память, выделенную подсистеме коммуникаций.

    Неблокирующая проверка завершения приема или передачи сообщения

    int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status)
    
    MPI_TEST(REQUEST, FLAG, STATUS, IERR)

    Входной параметр:

  • request - идентификатор операции обмена.
  • Выходные параметры:

  • flag - "истина", если операция, заданная идентификатором request, выполнена;
  • status - статус выполненной операции.
  • Если при вызове MPI_Test используется пустой или неактивный аргумент request, операция возвращает значение флага "истина" и пустой статус.

    Проверка завершения всех обменов

    int MPI_Waitall(int count, MPI_Request requests[], MPI_Status statuses [ ])
    MPI_WAITALL(COUNT, REQUESTS, STATUSES, IERR)

    Выполнение процесса блокируется до тех пор, пока все операции обмена, связанные с активными запросами в массиве requests, не будут выполнены. Возвращается статус этих операций. Статус обменов содержится в массиве statuses. count - количество запросов на обмен (размер массивов requests и statuses ).

    В результате выполнения подпрограммы MPI_Waitall запросы, сформированные неблокирующими операциями обмена, аннулируются, а соответствующим элементам массива присваивается значение MPI_REQUEST_NULL. Список может содержать пустые или неактивные запросы. Для каждого из них устанавливается пустое значение статуса.

    В случае неуспешного выполнения одной или более операций обмена подпрограмма MPI_Waitall возвращает код ошибки MPI_ERR_IN_STATUS и присваивает полю ошибки статуса значение кода ошибки соответствующей операции. Если операция выполнена успешно, полю присваивается значение MPI_SUCCESS, а если не выполнена, но и не было ошибки - значение MPI_ERR_PENDING. Последний случай соответствует наличию запросов на выполнение операции обмена, ожидающих обработки.

    Неблокирующая проверка завершения обменов

    int MPI_Testall(int count, MPI_Request requests[], int *flag, MPI_Status statuses[])
    MPI_TESTALL(COUNT, REQUESTS, FLAG, STATUSES, IERR)

    При вызове возвращается значение флага ( flag ) "истина", если все обмены, связанные с активными запросами в массиве requests, выполнены. Если завершены не все обмены, флагу присваивается значение "ложь", а массив statuses не определен. count - количество запросов.

    Каждому статусу, соответствующему активному запросу, присваивается значение статуса соответствующего обмена. Если запрос был сформирован операцией неблокирующего обмена, он аннулируется, а соответствующему элементу массива присваивается значение MPI_REQUEST_NULL. Каждому статусу, соответствующему пустому или неактивному запросу, присваивается пустое значение.

    Блокирующая проверка завершения любого числа обменов

    int MPI_Waitany(int count, MPI_Request requests[], int *index, MPI_Status *status)
    
    MPI_WAITANY(COUNT, REQUESTS, INDEX, STATUS, IERR)

    Выполнение процесса блокируется до тех пор, пока, по крайней мере, один обмен из массива запросов ( requests ) не будет завершен.

    Входные параметры:

  • requests - запрос;
  • count - количество элементов в массиве requests, а выходные: status и index .
  • Выходные параметры:

  • index - индекс запроса (в языке C это целое число от 0 до count - 1, а в языке FORTRAN от 1 до count ) в массиве requests ;
  • status - статус.
  • Если запрос на выполнение операции был сформирован неблокирующей операцией обмена, он аннулируется и ему присваивается значение MPI_REQUEST_NULL. Массив запросов может содержать пустые или неактивные запросы. Если в списке вообще нет активных запросов или он пуст, вызовы завершаются сразу со значением индекса MPI_UNDEFINED и пустым статусом.

    Проверка выполнения любого ранее инициализированного обмена

    int MPI_Testany(int count, MPI_Request requests[],  int *index, 
    	int *flag, MPI_Status *status)
    MPI_TESTANY(COUNT, REQUESTS, INDEX, FLAG, STATUS, IERR)

    Смысл и назначение параметров этой подпрограммы те же, что и для подпрограммы MPI_Waitany. Дополнительный аргумент flag, который принимает значение "истина", если одна из операций завершена. Блокирующая подпрограмма MPI_Waitany и неблокирующая MPI_Testany взаимозаменяемы, впрочем, как и другие аналогичные пары.

    Подпрограммы MPI_Waitsome и MPI_Testsome действуют аналогично подпрограммам MPI_Waitany и MPI_Testany, кроме случая, когда завершается более одного обмена. В подпрограммах MPI_Waitany и MPI_Testany обмен из числа завершенных выбирается произвольно, именно для него и возвращается статус, а для MPI_Waitsome и MPI_Testsome статус возвращается для всех завершенных обменов. Эти подпрограммы можно использовать для определения, сколько обменов завершено:

    int MPI_Waitsome(int incount, MPI_Request requests[],  int *outcount, 
    	int indices[], MPI_Status statuses[])
    MPI_WAITSOME(INCOUNT, REQUESTS, OUTCOUNT, INDICES, STATUSES, IERR)

    Здесь incount - количество запросов. В outcount возвращается количество выполненных запросов из массива requests, а в первых outcount элементах массива indices возвращаются индексы этих операций. В первых outcount элементах массива statuses возвращается статус завершенных операций. Если выполненный запрос был сформирован неблокирующей операцией обмена, он аннулируется. Если в списке нет активных запросов, выполнение подпрограммы завершается сразу, а параметру outcount присваивается значение MPI_UNDEFINED.

    Неблокирующая проверка выполнения обменов

    int MPI_Testsome(int incount, MPI_Request requests[],  int *outcount, 
    	int indices[], MPI_Status statuses[])
    
    MPI_TESTSOME(INCOUNT, REQUESTS, OUTCOUNT, INDICES, STATUSES, IERR)

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

    Запрос для стандартной передачи создается при вызове подпрограммы MPI_Send_init:

    int MPI_Send_init(void *buf,  int count, MPI_Datatype datatype, 
    	int dest,  int tag, MPI_Comm comm, MPI_Request *request)
    
    MPI_SEND_INIT(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERR)

    Входные параметры:

  • buf - адрес буфера передачи;
  • count - количество элементов;
  • datatype - тип элементов;
  • dest - ранг адресата;
  • tag - тег сообщения;
  • comm - коммуникатор.
  • Выходной параметр:

  • request - запрос на выполнение операции обмена.
  • Инициализация отложенного обмена

    int MPI_Start(MPI_Request *request)
    MPI_START(REQUEST, IERR)

    Входной параметр:

  • request - запрос на выполнение операции обмена.
  • Вызов MPI_Start с запросом на обмен, созданным MPI_Send_init, инициирует обмен с теми же свойствами, что и вызов подпрограммы MPI_Isend, а вызов MPI_Start с запросом, созданным MPI_Bsend_init, инициирует обмен аналогично вызову MPI_Ibsend. Сообщение, которое передано операцией, инициированной с помощью MPI_Start, может быть принято любой подпрограммой приема.

    Инициализация обменов, связанных с запросами на выполнение неблокирующей операции обмена в массиве requests

    int MPI_Startall(int count, MPI_request *requests)
    
    MPI_STARTALL(COUNT, REQUESTS, IERR)

    Аннулирование неблокирующих "ждущих" (ожидающих обработки) обменов

    int MPI_Cancel(MPI_request *request)
    
    MPI_CANCEL(REQUEST, IERR)

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

    Проверка, произведено ли аннулирование обмена, связанного с указанным статусом

    int MPI_Test_cancelled(MPI_Status *status,  int *flag)
    
    MPI_TEST_CANCELLED(STATUS, FLAG, IERR)

    Аннулирование запроса на выполнение операции (request)

    int MPI_Request_free(MPI_Request *request)
    
    MPI_REQUEST_FREE(REQUEST, IERR)

    При вызове помечает запрос на обмен для удаления и присваивает ему значение MPI_REQUEST_NULL. Операции обмена, связанной с этим запросом, дается возможность завершиться, а сам запрос удаляется только после завершения обмена.

    Коллективный обмен

    Участниками коллективного обмена являются более двух процессов.

    Широковещательная рассылка

    int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
    MPI_BCAST(BUFFER, COUNT, DATATYPE, ROOT, COMM, IERR)

    Параметры этой процедуры одновременно являются входными и выходными:

  • buffer - адрес буфера;
  • count - количество элементов данных в сообщении;
  • datatype - тип данных MPI ;
  • root - ранг главного процесса, выполняющего широковещательную рассылку;
  • comm - коммуникатор.
  • Схема распределения данных представлена на рис. 3.4.

    (рис 3.4) Распределение данных при широковещательной рассылке

    Синхронизация с помощью "барьера" (рис. 3.5)

    int MPI_Barrier(MPI_Comm comm)
    MPI_BARRIER(COMM, IERR)
    (рис 3.5) Синхронизация с "барьером"

    При синхронизации с барьером выполнение каждого процесса из данного коммуникатора приостанавливается до тех пор, пока все процессы не выполнят вызов процедуры синхронизации MPI_Barrier.

    Распределение данных

    int MPI_Scatter(void *sendbuf,  int sendcount, MPI_Datatype sendtype, void *rcvbuf,  int rcvcount, 
      MPI_Datatype rcvtype,  int root, MPI_Comm comm)
    MPI_SCATTER(SENDBUF,SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNT,   
    RCVTYPE, ROOT, COMM, IERR)

    Входные параметры:

  • sendbuf - адрес буфера передачи;
  • sendcount - количество элементов, пересылаемых каждому процессу (но не суммарное количество пересылаемых элементов);
  • sendtype - тип передаваемых данных;
  • rcvcount - количество элементов в буфере приема;
  • rcvtype - тип принимаемых данных;
  • root - ранг передающего процесса;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - адрес буфера приема.
  • Процесс с рангом root распределяет содержимое буфера передачи sendbuf среди всех процессов (рис. 3.6). Содержимое буфера передачи разбивается на несколько фрагментов, каждый из которых содержит sendcount элементов. Первый фрагмент передается процессу 0, второй процессу 1 и т. д. Аргументы send имеют значение только на стороне процесса root.

    (рис 3.6) Распределение данных при выполнении операции Scatter

    Сбор сообщений от остальных процессов в буфер главной задачи

    int MPI_Gather(void *sendbuf,  int sendcount, MPI_Datatype sendtype, void *rcvbuf,  
      int rcvcount, MPI_Datatype rcvtype,  int root, MPI_Comm comm)
    
    MPI_GATHER(SENDBUF, SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNT, RCVTYPE, ROOT, COMM, IERR)

    Каждый процесс в коммуникаторе comm пересылает содержимое буфера передачи sendbuf процессу с рангом root. Процесс root "склеивает" полученные данные в буфере приема (рис. 3.7). Порядок склейки определяется рангами процессов, то есть в результирующем наборе после данных от процесса 0 следуют данные от процесса 1, затем данные от процесса 2 и т. д. Аргументы rcvbuf, rcvcount и rcvtype играют роль только на стороне главного процесса. Аргумент rcvcount указывает количество элементов данных, полученных от каждого процесса (но не суммарное их количество). При вызове подпрограмм MPI_Scatter и MPI_Gather из разных процессов следует использовать общий главный процесс.

    (рис 3.7) Сбор данных при выполнении операции Gather

    Векторная подпрограмма распределения данных

    int MPI_Scatterv(void *sendbuf,  int *sendcounts,  int *displs, 
    	MPI_Datatype sendtype, void *rcvbuf, int rcvcount, 
    	MPI_Datatype rcvtype,  int root, MPI_Comm comm)
    
    MPI_SCATTERV(SENDBUF, SENDCOUNTS, DISPLS, SENDTYPE, RCVBUF, RCVCOUNT, RCVTYPE, ROOT, COMM, IERR)

    Входные параметры:

  • sendbuf - адрес буфера передачи;
  • sendcounts - целочисленный одномерный массив, содержащий количество элементов, передаваемых каждому процессу (индекс равен рангу адресата). Его длина равна количеству процессов в коммуникаторе;
  • displs - целочисленный массив, длина которого равна количеству процессов в коммуникаторе. Элемент с индексом i задает смещение относительно начала буфера передачи. Ранг адресата равен значению индекса i ;
  • sendtype - тип данных в буфере передачи;
  • rcvcount - количество элементов в буфере приема;
  • rcvtype - тип данных в буфере приема;
  • root - ранг передающего процесса;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - адрес буфера приема.
  • Сбор данных от всех процессов в заданном коммуникаторе и запись их в буфер приема с указанным смещением

    int MPI_Gatherv(void *sendbuf,  int sendcount, MPI_Datatype sendtype, void *recvbuf,  
      int *recvcounts,  int *displs, MPI_Datatype recvtype, int root, MPI_Comm comm)
    
    MPI_GATHERV(SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNTS, 
    	DISPLS, RECVTYPE, ROOT, COMM, IERR)

    Список параметров у этой подпрограммы похож на список параметров подпрограммы MPI_Scatterv. В обменах, выполняемых подпрограммами MPI_Allgather и MPI_Alltoall, нет главного процесса. Детали отправки и приема важны для всех процессов, участвующих в обмене.

    Сбор данных от всех процессов и распределение их всем процессам

    int MPI_Allgather(void *sendbuf,  int sendcount, MPI_Datatype sendtype, void *rcvbuf,  
      int rcvcount, MPI_Datatype rcvtype, MPI_Comm comm)
    
    MPI_ALLGATHER(SENDBUF, SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNT, RCVTYPE, COMM, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • sendcount - количество элементов в буфере передачи;
  • sendtype - тип передаваемых данных;
  • rcvcount - количество элементов, полученных от каждого процесса;
  • rcvtype - тип данных в буфере приема;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - адрес буфера приема.
  • Блок данных, переданный от j-го процесса, принимается каждым процессом и размещается в j-м блоке буфера приема recvbuf (рис. 3.8).

    (рис 3.8) Сбор и распределение данных при выполнении операции Allgather

    Пересылка данных по схеме "каждый - всем"

    int MPI_Alltoall(void *sendbuf,  int sendcount, MPI_Datatype sendtype, 
    	void *rcvbuf,  int rcvcount, MPI_Datatype rcvtype, MPI_Comm comm)
    
    MPI_ALLTOALL(SENDBUF, SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNT, RCVTYPE, COMM, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • sendcount - количество элементов данных, пересылаемых каждому процессу;
  • sendtype - тип данных в буфере передачи;
  • rcvcount - количество элементов данных, принимаемых от каждого процесса;
  • rcvtype - тип принимаемых данных;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - адрес буфера приема.
  • Векторными версиями MPI_Allgather и MPI_Alltoall являются подпрограммы MPI_Allgatherv и MPI_Alltoallv.

    Сбор данных от всех процессов и пересылка их всем процессам

    int MPI_Allgatherv(void *sendbuf,  int sendcount, MPI_Datatype sendtype, 
    	void *rcvbuf, int *rcvcounts, int *displs, MPI_Datatype rcvtype, MPI_Comm comm)
    
    MPI_ALLGATHERV(SENDBUF, SENDCOUNT, SENDTYPE, RCVBUF, RCVCOUNTS, DISPLS, RCVTYPE, COMM, IERR)

    Ее параметры совпадают с параметрами подпрограммы MPI_Allgather, за исключением дополнительного входного параметра displs. Это целочисленный одномерный массив, количество элементов в котором равно количеству процессов в коммуникаторе. Элемент массива с индексом i задает смещение относительно начала буфера приема recvbuf, в котором располагаются данные, принимаемые от процесса i. Блок данных, переданный от j-го процесса, принимается каждым процессом и размещается в j-м блоке буфера приема.

    Пересылка данных от всех процессов всем процессам со смещением

    int MPI_Alltoallv(void *sendbuf,  int *sendcounts,  int *sdispls, 
    	MPI_Datatype sendtype, void *rcvbuf,  int *rcvcounts,  int *rdispls, 
    	MPI_Datatype rcvtype, MPI_Comm comm)
    MPI_ALLTOALLV(SENDBUF, SENDCOUNTS, SDISPLS, SENDTYPE, RCVBUF, RCVCOUNTS, RDISPLS, RCVTYPE, COMM, IERR)

    Ее параметры аналогичны параметрам подпрограммы MPI_Alltoall, кроме двух дополнительных параметров:

  • sdispls - целочисленный массив, количество элементов в котором равно количеству процессов в коммуникаторе. Элемент j задает смещение относительно начала буфера, из которого данные передаются j -му процессу.
  • rdispls - целочисленный массив, количество элементов в котором равно количеству процессов в коммуникаторе. Элемент i задает смещение относительно начала буфера, в который принимается сообщение от i -го процесса.
  • Операция приведения, результат которой передается одному процессу

    int MPI_Reduce(void *buf, void *result, int count, 
    	MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)
    
    MPI_REDUCE(BUF, RESULT, COUNT, DATATYPE, OP, ROOT, COMM, IERR)

    Входные параметры:

  • buf - адрес буфера передачи;
  • count - количество элементов в буфере передачи;
  • datatype - тип данных в буфере передачи;
  • op - операция приведения;
  • root - ранг главного процесса;
  • comm - коммуникатор.
  • MPI_Reduce применяет операцию приведения к операндам из buf, а результат каждой операции помещается в буфер результата result (рис. 3.9). MPI_Reduce должна вызываться всеми процессами в коммуникаторе comm, а аргументы count, datatype и op в этих вызовах должны совпадать.

    (рис 3.9) Распределение данных при выполнении операции приведения

    Предопределенные операции приведения приведены в табл. 3.3.

    Определение собственных глобальных операций

    int MPI_Op_create(MPI_User_function *function, int commute, MPI_Op *op)
    MPI_OP_CREATE(FUNCTION, COMMUTE, OP, IERR)

    Входные параметры:

  • function - пользовательская функция;
  • commute - флаг, которому присваивается значение "истина", если операция коммутативна (результат не зависит от порядка операндов).
  • Описание типа пользовательской функции выглядит следующим образом:

    typedef void (MPI_User_function)(void *a, void *b, int *len, MPI_Datatype *dtype)

    Здесь операция определяется так:

    b[I] = a[I] op b[I] 
    для I = 0,  ...,   len-1.
    Предопределенные операции приведения MPI
    Операция Описание
    MPI_MAX Определение максимальных значений элементов одномерных массивов целого или вещественного типа
    MPI_MIN Определение минимальных значений элементов одномерных массивов целого или вещественного типа
    MPI_SUM Вычисление суммы элементов одномерных массивов целого, вещественного или комплексного типа
    MPI_PROD Вычисление поэлементного произведения одномерных массивов целого, вещественного или комплексного типа
    MPI_LAND Логическое "И"
    MPI_BAND Битовое "И"
    MPI_LOR Логическое "ИЛИ"
    MPI_BOR Битовое "ИЛИ"
    MPI_LXOR Логическое исключающее "ИЛИ"
    MPI_BXOR Битовое исключающее "ИЛИ"
    MPI_MAXLOC Максимальные значения элементов одномерных массивов и их индексы
    MPI_MINLOC Минимальные значения элементов одномерных массивов и их индексы

    Удаление пользовательской функции

    int MPI_Op_free(MPI_Op *op)
    MPI_OP_FREE(OP, IERR)

    После завершения вызова op присваивается значение MPI_OP_NULL.

    Одновременные сбор и распределение данных

    int MPI_Reduce_scatter(void *sendbuf, void *rcvbuf, int *rcvcounts, 
    MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
    MPI_REDUCE_SCATTER(SENDBUF,RCVBUF,RCVCOUNTS,DATATYPE,OP,COMM, IERR)

    Входные параметры:

  • sendbuf - стартовый адрес буфера приема;
  • rcvcounts - целочисленный одномерный массив, который задает количество элементов в результирующем массиве, распределяемом каждому процессу. Этот массив должен быть одинаковым во всех процессах, вызывающих данную подпрограмму;
  • datatype - тип данных в буфере приема;
  • op - операция;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - стартовый адрес буфера приема.
  • Каждая задача получает не весь результирующий массив, а его часть.

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

    int MPI_Allreduce(void *sendbuf, void *rcvbuf,  int count, 
    MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
    
    MPI_ALLREDUCE(SENDBUF, RCVBUF, COUNT, DATATYPE, OP, COMM, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • count - количество элементов в буфере передачи;
  • datatype - тип передаваемых данных;
  • op - операция приведения;
  • comm - коммуникатор. Выходной параметр:
  • rcvbuf - стартовый адрес буфера приема.
  • При аварийном завершении подпрограмма может возвращать код ошибки MPI_ERR_OP (некорректная операция). Это происходит, если применяется операция, которая не является предопределенной и которая не создана предшествующим вызовом подпрограммы MPI_Op_create.

    Операции сканирования (частичной редукции)

    int MPI_Scan(void *sendbuf,  void *rcvbuf,  int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
    
    MPI_SCAN(SENDBUF, RCVBUF, COUNT, DATATYPE, OP, COMM, IERR)

    Входные параметры:

  • sendbuf - начальный адрес буфера передачи;
  • count - количество элементов во входном буфере;
  • datatype - тип данных во входном буфере;
  • op - операция;
  • comm - коммуникатор.
  • Выходной параметр:

  • rcvbuf - стартовый адрес буфера приема.
  • Управление коммуникаторами

    Стандартный коммуникатор MPI_COMM_WORLD создается автоматически при запуске параллельной программы на выполнение. Напомним, что имеются также стандартные коммуникаторы:

  • MPI_COMM_SELF - коммуникатор, содержащий только вызывающий процесс;
  • MPI_COMM_NULL - пустой коммуникатор.
  • Получение доступа к группе group, связанной с коммуникатором comm

    int MPI_Comm_group(MPI_Comm comm, MPI_Group *group)
    
    MPI_COMM_GROUP(COMM, GROUP, IERR)

    Выходной параметр - группа. Для выполнения операций с группой к ней сначала необходимо получить доступ.

    Создание новой группы newgroup из n процессов, входящих в группу oldgroup

    int MPI_Group_incl(MPI_Group oldgroup,  int n,  int *ranks, MPI_Group *newgroup)
    
    MPI_GROUP_INCL(OLDGROUP,  N,   RANKS,  NEWGROUP,   IERR)

    Ранги процессов содержатся в массиве ranks. В новую группу войдут процессы с рангами ranks[0], ranks[n - 1],причем рангу i в новой группе соответствует ранг ranks[i] в старой группе. При n = 0 создается пустая группа MPI_GROUP_EMPTY. С помощью данной подпрограммы можно не только создать новую группу, но и изменить порядок процессов в старой группе.

    Создание группы newgroup исключением из исходной группы (group) процессы с рангами ranks[0] ..., ranks[n - 1]

    int MPI_Group_excl(MPI_Group oldgroup,  int n,  int *ranks, MPI_Group *newgroup)
    MPI_GROUP_EXCL(OLDGROUP, N, RANKS, NEWGROUP, IERR)

    При n = 0 новая группа тождественна старой.

    Создание группы newgroup из группы group добавлением в нее n процессов, ранг которых указан в массиве ranks

    int MPI_Group_range_incl(MPI_Group oldgroup,  int n,  int ranks[][3], MPI_Group *newgroup)
    
    MPI_GROUP_RANGE_INCL(OLDGROUP, N, RANKS, NEWGROUP, IERR)

    Массив ranks состоит из целочисленных триплетов вида (первый_1, последний_1, шаг_1), ..., (первый_n, последний_n, шаг_n). В новую группу войдут процессы с рангами (по первой группе) первый_1, первый_1 + шаг_1, ....

    Создание группы newgroup из группы group исключением из нее n процессов, ранг которых указан в массиве ranks

    int MPI_Group_range_excl(MPI_Group group,  int n,  int ranks[][3], MPI_Group *newgroup)
    
    MPI_GROUP_RANGE_EXCL(GROUP, N, RANKS, NEWGROUP, IERR)

    Массив ranks устроен так же, как аналогичный массив в подпрограмме MPI_Group_range_incl.

    Создание новой группы (newgroup) из разности двух групп (group1) и (group2)

    int MPI_Group_difference(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup)
    
    MPI_GROUP_DIFFERENCE(GROUP1, GROUP2, NEWGROUP, IERR)

    Создание новой группы (newgroup) из пересечения групп group1 и group2

    int MPI_Group_intersection(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup)
    
    MPI_GROUP_INTERSECTION(GROUP1, GROUP2, NEWGROUP, IERR)

    Создание группы (newgroup) объединением групп group1 и group2

    Int MPI_Group_union(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup)
    
    MPI_GROUP_UNION(GROUP1, GROUP2, NEWGROUP, IERR)

    Имеются и другие подпрограммы-конструкторы новых групп.

    Уничтожение группы group

    int MPI_Group_free(MPI_Group *group)
    
    MPI_GROUP_FREE(GROUP, IERR)

    Определение количества процессов (size) в группе (group)

    MPI_GROUP_SIZE(GROUP,   SIZE,   IERR)
    int MPI_Group_size(MPI_Group group,  int *size)

    Определение ранга (rank) процесса в группе group

    int MPI_Group_rank(MPI_Group group, int *rank)
    
    MPI_GROUP_RANK(GROUP, RANK, IERR)

    Если процесс не входит в указанную группу, возвращается значение MPI_UNDEFINED.

    Преобразование ранга процесса в одной группе в его ранг относительно другой группы

    int MPI_Group_translate_ranks(MPI_Group group1, int n, 
    	int *ranks1, MPI_Group group2, int *ranks2)
    MPI_GROUP_TRANSLATE_RANKS(GROUP1, N, RANKS1, GROUP2, RANKS2, IERR)

    Сравнение групп group1 и group2

    int MPI_Group_compare(MPI_Group group1, MPI_Group group2,  int *result)
    
    MPI_GROUP_COMPARE(GROUP1, GROUP2, RESULT, IERR)

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

    Создание дубликата уже существующего коммуникатора oldcomm int

    MPI_Comm_dup(MPI_Comm oldcomm, MPI_Comm *newcomm)
    
    MPI_COMM_DUP(OLDCOMM, NEWCOMM, IERR)

    В результате вызова данной подпрограммы создается новый коммуникатор ( newcomm ) с той же группой процессов, с теми же атрибутами, но с другим контекстом. Подпрограмма может применяться как к интра-, так и к интеркоммуникаторам.

    Создание нового коммуникатора (newcomm) из подмножества процессов (group) другого коммуникатора (oldcomm)

    int MPI_Comm_create(MPI_Comm oldcomm, MPI_Group group, MPI_Comm *newcomm)
    
    MPI_COMM_CREATE(OLDCOMM, GROUP, NEWCOMM, IERR)

    Вызов этой подпрограммы должны выполнить все процессы из старого коммуникатора, даже если они не входят в группу group, с одинаковыми аргументами. Если одновременно создаются несколько коммуникаторов, они должны создаваться в одной последовательности всеми процессами.

    Создание нескольких коммуникаторов сразу методом расщепления

    int MPI_Comm_split(MPI_Comm oldcomm,  int split,  int rank, MPI_Comm* newcomm)
    MPI_COMM_SPLIT(OLDCOMM, SPLIT, RANK, NEWCOMM, IERR)

    Группа процессов, связанных с коммуникатором oldcomm, разбивается на непересекающиеся подгруппы, по одной для каждого значения аргумента split. Процессы с одинаковым значением split образуют новую группу. Ранг в новой группе определяется значением rank. Если процессы A и B вызывают MPI_Comm_split с одинаковым значением split, а аргумент rank, переданный процессом A, меньше, чем аргумент, переданный процессом B, ранг A в группе, соответствующей новому коммуникатору, будет меньше ранга процесса B. Если же в вызовах используется одинаковое значение rank, система присвоит ранги произвольно. Для каждой подгруппы создается собственный коммуникатор newcomm.

    MPI_Comm_split должны вызвать все процессы из старого коммуникатора, даже если они не войдут в новый коммуникатор. Для этого в качестве аргумента split в подпрограмму передается предопределенная константа MPI_UNDEFINED. Соответствующие процессы вернут в качестве нового коммуникатора значение MPI_COMM_NULL. Новые коммуникаторы, созданные подпрограммой MPI_Comm_split, не пересекаются, однако с помощью повторных вызовов подпрограммы MPI_Comm_split можно создавать и перекрывающиеся коммуникаторы.

    Пометить коммуникатор comm для удаления

    int MPI_Comm_free(MPI_Comm *comm)
    
    MPI_COMM_FREE(COMM, IERR)

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

    Сравнение двух коммуникаторов (comm1) и (comm2)

    int MPI_Comm_compare(MPI_Comm comm1, MPI_Comm comm2,  int *result)
    
    MPI_COMM_COMPARE(COMM1, COMM2, RESULT, IERR)

    Выходной параметр:

  • result - целое значение, которое равно MPI_IDENT, если контексты и группы коммуникаторов совпадают; MPI_CONGRUENT, если совпадают только группы; MPI_SIMILAR и MPI_UNEQUAL, если не совпадают ни группы, ни контексты.
  • В качестве аргументов нельзя использовать пустой коммуникатор MPI_COMM_NULL.

    Присвоение коммуникатору comm строкового имени name

    int MPI_Comm_set_name(MPI_Comm com,  char *name) 
    MPI_COMM_SET_NAME(COM, NAME, IERR)

    Определение имени коммуникатора

    int MPI_Comm_get_name(MPI_Comm comm, char *name, int *reslen)
    MPI_COMM_GET_NAME(COMM, NAME, RESLEN, IERR)

    Выходной параметр:

  • name - строковое имя коммуникатора comm.;
  • reslen - длина имени.
  • Имя представляет собой массив символьных значений, длина которого должна быть не более MPI_MAX_NAME_STRING.

    Проверка, является ли коммуникатор comm (входной параметр) интеркоммуникатором

    int MPI_Comm_test_inter(MPI_Comm comm,  int *flag)
    
    MPI_COMM_TEST_INTER(COMM, FLAG, IERR)

    Выходной параметр:

  • flag - значение флага "истина", если аргументом является интеркоммуникатор.
  • Создание интракоммуникатора newcomm из интеркоммуникатора oldcomm

    int MPI_Intercomm_merge(MPI_Comm oldcomm,  int high, MPI_Comm *newcomm)
    
    MPI_INTERCOMM_MERGE(OLDCOMM, HIGH, NEWCOMM, IERR)

    Параметр high используется для упорядочения групп обоих интракоммуникаторов в comm при создании нового коммуникатора.

    Получение доступа к удаленной группе, связанной с интеркоммуникатором comm int

    MPI_Comm_remote_group(MPI_Comm comm, MPI_Group *group)
    
    MPI_COMM_REMOTE_GROUP(COMM, GROUP, IERR)

    Выходной параметр:

  • group - удаленная группа.
  • Определение размера удаленной группы, связанной с интеркоммуникатором comm int

    MPI_Comm_remote_size(MPI_Comm comm,  int *size)
    
    MPI_COMM_REMOTE_SIZE(COMM, SIZE, IERR)

    Выходной параметр:

  • size - количество процессов в области взаимодействия, связанной с коммуникатором comm.
  • Создание интеркоммуникатора

    int MPI_Intercomm_create(MPI_Comm local_comm,  int local_leader, 
    	MPI_Comm peer_comm, int remote_leader, int tag, MPI_Comm *new_intercomm)
    MPI_INTERCOMM_CREATE(LOCAL_COMM, LOCAL_LEADER, PEER_COMM, REMOTE_LEADER, TAG, NEW_INTERCOMM, IERR)

    Входные параметры:

  • local_comm - локальный интракоммуникатор;
  • local_leader - ранг лидера в локальном коммуникаторе (обычно 0);
  • peer_comm - удаленный коммуникатор;
  • remote_leader - ранг лидера в удаленном коммуникаторе (обычно 0);
  • tag - тег интеркоммуникатора, используемый лидерами обеих групп для обменов в контексте родительского коммуникатора.
  • Выходной параметр:

  • new_intercomm - интеркоммуникатор.
  • "Джокеры" в качестве параметров использовать нельзя. Вызов этой подпрограммы должен выполняться в обеих группах процессов, которые должны быть связаны между собой. В каждом из этих вызовов используется локальный интракоммуникатор, соответствующий данной группе процессов. При работе с MPI_Intercomm_create локальная и удаленная группы процессов не должны пересекаться, иначе возможны "тупики".

    Топологии

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

    Создание нового коммуникатора comm_cart посредством наделения декартовой топологией исходный коммуникатор comm_old (рис. 3.10)

    int MPI_Cart_create(MPI_Comm comm_old,  int ndims,  int *dims,  
    	int *periods,  int reorder, MPI_Comm *comm_cart)
    MPI_CART_CREATE(COMM_OLD, NDIMS, DIMS, PERIODS, REORDER, COMM_CART, IERR)

    Входные параметры:

  • comm_old - исходный коммуникатор;
  • ndims - размерность декартовой решетки;
  • dims -целочисленный массив, состоящий из ndims элементов, задающий количество процессов в каждом измерении;
  • periods - логический массив из ndims элементов, который определяет, является ли решетка периодической (значение true ) вдоль каждого измерения;
  • reorder - при значении этого параметра "истина", системе разрешено менять порядок нумерации процессов.
  • Информация о структуре декартовой топологии содержится в параметрах ndims, dims и periods. MPI_Cart_create является коллективной операцией (эту подпрограмму должны вызывать все процессы из коммуникатора, наделяемого декартовой топологией).

    (рис 3.10) Декартова топология

    Определение декартовых координат процесса по его рангу в группе

    int MPI_Cart_coords(MPI_Comm comm,  int rank,  int maxdims,  int *coords)
    
    MPI_CART_COORDS(COMM, RANK, MAXDIMS, COORDS, IERR)

    Входные параметры:

  • comm - коммуникатор, наделенный декартовой топологией;
  • rank - ранг процесса в comm ;
  • maxdims - длина вектора coords в вызывающей программе.
  • Выходной параметр:

  • coords - одномерный целочисленный массив (его размер равен ndims ), содержащий декартовы координаты процесса.
  • Определение ранга процесса (rank) по его декартовым координатам в коммуникаторе comm int

    MPI_Cart_rank(MPI_Comm comm, int *coords, int *rank)
    
    MPI_CART_RANK(COMM, COORDS, RANK, IERR)

    Входной параметр:

  • coords - целочисленный массив размера ndims, задающий декартовы координаты процесса. Как MPI_Cart_rank, так и MPI_Cart_coords локальны.
  • Расщепление коммуникатора comm на подгруппы, соответствующие декартовым подрешеткам меньшей размерности

    int MPI_Cart_sub(MPI_Comm comm, int *remain_dims, MPI_Comm *comm_new)
    MPI_CART_SUB(COMM, REMAIN_DIMS, COMM_NEW, IERR)

    I -й элемент массива remain_dims определяет, содержится ли I-е измерение в подрешетке ("истина"). Выходной параметр:

  • newcomm - коммуникатор, содержащий подрешетку, которой принадлежит данный процесс.
  • Подпрограмма MPI_Cart_sub используется только с коммуникатором, наделенным декартовой топологией.

    Получение информации о декартовой топологии, связанной с коммуникатором comm

    int MPI_Cart_get(MPI_Comm comm, int maxdims, int *dims, int *periods, int *coords)
    
    MPI_CART_GET(COMM, MAXDIMS, DIMS, PERIODS, COORDS, IERR)

    Входной параметр:

  • maxdims - задает длину массивов dims, periods и vectors в вызывающей программе
  • Выходные параметры:

  • dims - целочисленный массив, задающий количество процессов для каждого измерения;
  • periods - массив логических значений, задающих периодичность ( true, если решетка периодическая) для каждого измерения;
  • coords - целочисленный массив, задающий декартовы координаты вызывающего подпрограмму процесса.
  • Определение ранга процесса (newrank) в декартовой топологии после переупорядочения процессов

    int MPI_Cart_map(MPI_Comm comm_old, int ndims, int *dims, int *periods, int *newrank)
    MPI_CART_MAP(COMM_OLD, NDIMS, DIMS, PERIODS, NEWRANK, IERR)

    Входные параметры:

  • comm - коммуникатор;
  • ndims - размерность декартовой решетки;
  • dims - целочисленный массив, состоящий из ndims элементов, который определяет количество процессов вдоль каждого измерения;
  • periods - логический массив размера ndims, определяющий периодичность решетки вдоль каждого измерения.
  • Если процесс не принадлежит решетке, подпрограмма возвращает значение MPI_UNDEFINED.

    Определение рангов источника (source) сообщения, которое должно быть принято, и адресата (dest), который должен получить сообщение для заданного направления сдвига (direction) и его величины (disp)

    int MPI_Cart_shift(MPI_Comm comm, int direction, int displ, int *source,  int *dest)
    MPI_CART_SHIFT(COMM, DIRECTION, DISPL, SOURCE, DEST, IERR)

    Для n -мерной декартовой решетки значение аргумента direction должно находиться в пределах от 0 до n-1.

    Определение размерности (ndims) декартовой топологии, связанной с коммуникатором

    int MPI_Cartdim_get(MPI_Comm comm,  int *ndims) MPI_CARTDIM_GET(COMM, NDIMS, IERR)

    Создание нового коммуникатора comm_graph, наделенного топологией графа (рис. 3.11)

    int MPI_Graph_create(MPI_Comm comm,  int nnodes,  int *index, 
    	int *edges,  int reorder, MPI_Comm *comm_graph)
    
    MPI_GRAPH_CREATE(COMM, NNODES, INDEX, EDGES, REORDER, COMM_GRAPH, IERR)

    Входные параметры:

  • comm - исходный коммуникатор, не наделенный топологией;
  • nnodes - количество вершин графа;
  • index - целочисленный одномерный массив, содержащий порядок каждого узла (количество связанных с ним ребер);
  • edges - целочисленный одномерный массив, описывающий ребра графа;
  • reorder - значение "истина" разрешает изменение порядка нумерации процессов.
  • (рис 3.11) Виртуальная топология графа

    Определение вершин графа, соседних с данной

    int MPI_Graph_neighbors(MPI_Comm comm, int rank, int maxneighbors, int *neighbors)
    MPI_GRAPH_NEIGHBORS(COMM,  RANK,  MAXNEIGHBORS,  NEIGHBORS,   IERR)

    Входные параметры:

  • comm - коммуникатор с топологией графа;
  • rank - ранг процесса в группе коммуникатора comm ;
  • maxneighbors - размер массива neighbors.
  • Выходной параметр:

  • neighbors - массив, содержащий ранги процессов, соседних с данным.
  • Определение количества соседей (nneighbors) узла, связанного с топологией графа

    int MPI_Graph_neighbors_count(MPI_Comm comm,  int rank,  int *nneighbors)
    
    MPI_GRAPH_NEIGHBORS_COUNT(COMM, RANK, NNEIGHBORS, IERR)

    Входные параметры:

  • comm - коммуникатор;
  • rank - ранг процесса-узла.
  • Получение информации о топологии графа, связанной с коммуникатором comm

    int MPI_Graph_get(MPI_Comm comm, int maxindex, int maxedges, int *index,  int *edges)
    
    MPI_GRAPH_GET(COMM, MAXINDEX, MAXEDGES, INDEX, EDGES, IERR)

    Входные параметры:

  • comm - коммуникатор;
  • maxindex - длина массива index в вызывающей программе;
  • maxedges - длина массива edges в вызывающей программе.
  • Выходные параметры:

  • index - целочисленный массив, содержащий структуру графа (см. описание подпрограммы MPI_Graph_create );
  • edges - целочисленный массив, содержащий сведения о ребрах графа.
  • Определение ранга процесса в топологии графа после переупорядочения (newrank)

    int MPI_Graph_map(MPI_Comm comm,  int nnodes,  int *index,  int *edges, int *newrank)
    MPI_GRAPH_MAP(COMM, NNODES, INDEX, EDGES, NEWRANK, IERR)

    Входные параметры:

  • comm - коммуникатор;
  • nnodes - количество вершин графа;
  • index - целочисленный массив, задающий структуру графа (см. описание подпрограммы MPI_Graph_create );
  • edges - целочисленный массив, задающий ребра графа.
  • Если процесс не принадлежит графу, подпрограмма возвращает значение MPI_UNDEFINED.

    Получение информации о топологии графа, связанной с коммуникатором comm int

    MPI_Graphdims_get(MPI_Comm comm, int *nnodes, int *nedges)
    
    MPI_GRAPHDIMS_GET(COMM, NNODES, NEDGES, IERR)

    Выходные параметры:

  • nnodes - количество вершин графа;
  • nedges - количество ребер графа.
  • Определение типа топологии (toptype), связанной с коммуникатором comm int

    MPI_Topo_test(MPI_Comm comm,  int *toptype)
    
    MPI_TOPO_TEST(COMM, TOPTYPE, IERR)

    Выходной параметр:

  • toptype - тип топологии (значения MPI_CART для декартовой топологии и MPI_GRAPH для топологии графа).
  • Производные типы

    Производные типы данных MPI используются для пересылки данных, элементы которых располагаются в памяти не подряд, а с "пробелами". Производный тип сначала создается с помощью вызова подпрограммы-конструктора, а потом регистрируется. Перед завершением программы производный тип должен быть удален.

    Конструктор векторного типа

    int MPI_Type_vector(int count,  int blocklen,  int stride, 
    	MPI_Datatype oldtype, MPI_Datatype *newtype)
    MPI_TYPE_VECTOR(COUNT, BLOCKLEN, STRIDE, OLDTYPE, NEWTYPE, IERR)

    Входные параметры:

  • count - количество блоков (неотрицательное целое значение);
  • blocklen - длина каждого блока (количество элементов, неотрицательное целое);
  • stride - количество элементов, расположенных между началом предыдущего и началом следующего блока ("гребенка");
  • oldtype - базовый тип.
  • Выходной параметр:

  • newtype - идентификатор нового типа, который назначается программистом.
  • Исходные данные однотипные.

    Конструктор векторного типа (рис. 3.12)

    int MPI_Type_hvector(int count, int blocklen, MPI_Aint stride, 
    	MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_HVECTOR(COUNT, BLOCKLEN, STRIDE, OLDTYPE, NEWTYPE, IERR)

    Смысл и назначение параметров этой подпрограммы совпадают с подпрограммой MPI_Type_vector, только значение параметра stride задается в байтах.

    (рис 3.12) Векторный производный тип

    Конструктор структурного типа

    int MPI_Type_struct(int count,  int blocklengths[], MPI_Aint indices[], 
    	MPI_Datatype oldtypes[], MPI_Datatype *newtype)
    
    MPI_TYPE_STRUCT(COUNT, BLOCKLENGTHS, INDICES, OLDTYPES, NEWTYPE, IERR)

    Входные параметры:

  • count - задает количество элементов в производном типе, а также длину массивов
  • oldtypes, indices и blocklengths ;
  • blocklengths - количество элементов в каждом блоке (массив);
  • indices - смещение каждого блока в байтах (массив);
  • oldtypes - тип элементов в каждом блоке (массив).
  • Выходной параметр:

  • newtype - идентификатор производного типа.
  • MPI_Aint - представляет собой скалярный тип, длина которого имеет размер, одинаковый с указателем.

    Конструктор индексированного типа

    int MPI_Type_indexed(int count,  int blocklens[],  int indices[], 
    	MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_INDEXED(COUNT, BLOCKLENS, INDICES, OLDTYPE, NEWTYPE, IERR)

    Входные параметры:

  • count - количество блоков, одновременно длина массивов indices и blocklens ;
  • blocklens - количество элементов в каждом блоке;
  • indices - смещение каждого блока, которое задается в количестве ячеек базового типа (целочисленный массив);
  • oldtype - базовый тип. Выходной параметр:
  • newtype - идентификатор производного типа.
  • Конструктор индексированного типа

    int MPI_Type_hindexed(int count, int blocklens[], MPI_Aint indices[], 
    	MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_HINDEXED(COUNT, BLOCKLENS, INDICES, OLDTYPE, NEWTYPE, IERR)

    Смещения indices задаются в байтах:

    Конструктор типа данных с непрерывным расположением элементов

    int MPI_Type_contiguous(int count, MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_CONTIGUOUS(COUNT, OLDTYPE, NEWTYPE, IERR)

    Входные параметры:

  • count - счетчик повторений;
  • oldtype - базовый тип
  • Выходной параметр:

  • newtype - идентификатор нового типа.
  • Конструктор индексированного типа с блоками постоянного размера

    int MPI_Type_create_indexed_block(int count, int blocklength,  
    	int displacements[], MPI_Datatype oldtype, MPI_Datatype *newtype)
    MPI_TYPE_CREATE_INDEXED_BLOCK(COUNT, BLOCKLENGTH, DISPLACEMENTS, OLDTYPE,   NEWTYPE, IERR)

    Входные параметры:

  • count - количество блоков и размер массивов indices и blocklens ;
  • blocklength - количество элементов в каждом блоке;
  • displacements - смещение каждого блока в единицах длины типа oldtype (целочисленный массив);
  • oldtype - базовый тип.
  • Выходной параметр:

  • newtype - идентификатор производного типа.
  • Конструктор типа данных, соответствующего подмассиву многомерного массива

    int MPI_Type_create_subarray(int ndims, int *sizes, int *subsizes, 
    	int *starts, int order, MPI_Datatype oldtype, MPI_Datatype *newtype)
    
    MPI_TYPE_CREATE_SUBARRAY(NDIMS, SIZES, SUBSIZES, STARTS, ORDER, OLDTYPE, NEWTYPE, IERR)

    Входные параметры:

  • ndims - размерность массива;
  • sizes - количество элементов типа oldtype в каждом измерении полного массива;
  • subsizes - количество элементов типа oldtype в каждом измерении подмассива;
  • starts - стартовые координаты подмассива в каждом измерении;
  • order - флаг, задающий переупорядочение;
  • oldtype - базовый тип.
  • Выходной параметр:

  • newtype - новый тип.
  • Регистрация производного типа datatype, сконструированного программистом

    int MPI_Type_commit(MPI_Datatype *datatype)
    
    MPI_TYPE_COMMIT(DATATYPE, IERR)

    Удаление производного типа datatype

    int MPI_Type_free(MPI_Datatype *datatype)
    
    MPI_TYPE_FREE(DATATYPE,   IERR)

    Базовые типы данных не могут быть удалены.

    Определение размера типа datatype в байтах (объем памяти, занимаемый одним элементом данного типа)

    int MPI_Type_size(MPI_Datatype datatype,  int *size)
    
    MPI_TYPE_SIZE(DATATYPE, SIZE, IERR)

    Выходной параметр - размер size.

    Определение количества элементов данных в одном объекте типа datatype (его экстент)

    int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint *extent)
    
    MPI_TYPE_EXTENT(DATATYPE, EXTENT, IERR)

    Выходной параметр - extent.

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

    Определение адреса (address) по заданному положению (location)

    int MPI_Address(void *location, MPI_Aint *address)
    
    MPI_ADDRESS(LOCATION, ADDRESS, IERR)

    Может использоваться в программах на языках C и FORTRAN. В C она обычно возвращает тот же адрес, что и оператор , хотя иногда это не так. Данная подпрограмма может понадобиться в программе на языке FORTRAN, в C есть собственные средства для определения адреса.

    Определение фактических параметров, использованных при создании производного типа

    int MPI_Type_get_contents(MPI_Datatype datatype,  int max_integers,  
    	int max_addresses,  int max_datatypes,  int *integers, MPI_Aint *addresses, MPI_Datatype *datatypes)
    
    MPI_TYPE_GET_CONTENTS(DATATYPE, MAX_INTEGERS, MAX_ADDRESSES,
    MAX_DATATYPES, INTEGERS, ADDRESSES, DATATYPES,   IERR)

    Входные параметры:

  • datatype - идентификатор типа;
  • max_integers - количество элементов в массиве integers ;
  • max_addresses - количество элементов в массиве addresses ;
  • max_datatypes - количество элементов в массиве datatypes.
  • Выходные параметры:

  • integers - содержит целочисленные аргументы, использованные при конструировании указанного типа;
  • addresses - содержит аргументы address, использованные при конструировании указанного типа;
  • datatypes - содержит аргументы datatype, использованные при конструировании указанного типа.
  • Определение нижней границы типа данных datatype

    int MPI_Type_lb(MPI_Datatype datatype, MPI_Aint Misplacement)
    
    MPI_TYPE_LB(DATATYPE, DISPLACEMENT, IERR)

    Выходной параметр:

  • displacement - смещение (в байтах) нижней границы относительно источника.
  • Определение верхней границы типа

    int MPI_Type_ub(MPI_Datatype datatype, MPI_Aint Misplacement)
    MPI_TYPE_UB(DATATYPE, DISPLACEMENT, IERR)

    Упаковка данных

    int MPI_Pack(void *inbuf, int incount, MPI_Datatype datatype, void *outbuf,  
    	int outcount,  int *position, MPI_Comm comm)
    
    MPI_PACK(INBUF, INCOUNT, DATATYPE, OUTBUF, OUTCOUNT, POSITION, COMM, IERR)

    При вызове incount элементов указанного типа выбираются из входного буфера и упаковываются в выходном буфере, начиная с положения position.

    Входные параметры:

  • inbuf - начальный адрес входного буфера;
  • incount - количество входных данных;
  • datatype - тип каждого входного элемента данных;
  • outcount - размер выходного буфера в байтах;
  • position - текущее положение в буфере в байтах;
  • comm - коммуникатор для упакованного сообщения.
  • Выходной параметр:

  • outbuf - стартовый адрес выходного буфера.
  • Распаковка данных

    int MPI_Unpack(void *inbuf, int insize, int *position, void *outbuf, 
    	int outcount, MPI_Datatype datatype, MPI_Comm comm)
    
    MPI_UNPACK(INBUF, INSIZE, POSITION, OUTBUF, OUTCOUNT, DATATYPE, COMM,
    IERR)

    Входные параметры:

  • inbuf - стартовый адрес входного буфера;
  • insize - размер входного буфера в байтах;
  • position - текущее положение в байтах;
  • outcount - количество данных, которые должны быть распакованы;
  • datatype - тип каждого выходного элемента данных;
  • comm - коммуникатор для упаковываемого сообщения.
  • Выходной параметр:

  • outbuf - стартовый адрес выходного буфера.
  • Определение объема памяти size (в байтах), необходимого для распаковки сообщения

    int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size)
    MPI_PACK_SIZE(INCOUNT, DATATYPE, COMM, SIZE, IERR)

    Входные параметры:

  • incount - аргумент count, использованный при упаковке;
  • datatype - тип упакованных данных;
  • comm - коммуникатор.
  • Атрибуты

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

    Создание нового ключа атрибута keyval (выходной параметр)

    int MPI_Keyval_create(MPI_Copy_function *copy_fn, MPI_Delete_function *delete_fn, 
    	int *keyval, void *extra_state)
    
    MPI_KEYVAL_CREATE(COPY_FN,DELETE_FN, KEYVAL, EXTRA_STATE, IERR)

    Ключи уникальны для каждого процесса и не видны пользователю, хотя явным образом хранятся в виде целых значений. Будучи однажды задан, ключ может быть использован для задания атрибутов и доступа к ним в любом коммуникаторе. Функция copy_fn вызывается, когда коммуникатор дублируется подпрограммой MPI_Comm_dup, а функция delete_fn используется для удаления. Параметр extra_state задает дополнительную информацию (состояние) для функций копирования и удаления.

    Задание типа функции MPI_Copy_function

    typedef int MPI_Copy_function(MPI_Comm oldcomm,  int keyval,  
    	void *extra_state, void *attribute_val_in, void *attribute_val_out,  int *flag)
    
    SUBROUTINE COPY_FUNCTION(OLDCOMM, KEYVAL, EXTRA_STATE,
    ATTTRIBUTE_VAL_IN, ATTRIBUTE_VAL_OUT, FLAG, IERR)

    Функция копирования вызывается для каждого значения ключа в исходном коммуникаторе в произвольном порядке. Каждое обращение к функции копирования выполняется со значением ключа и соответствующим ему атрибутом. Если она возвращает значение флага flag = 0, атрибут удаляется из продублированного коммуникатора. В противном случае ( flag = 1 ) устанавливается новое значение атрибута, равное значению, возвращенному в параметре attribute_val_out.

    Функцию copy_fn в языках C или FORTRAN можно определить значениями MPI_NULL_COPY_FN или MPI_DUP_FN. Значение MPI_NULL_COPY_FN является функцией, которая не выполняет никаких действий, только возвращает значение флага flag = 0 и MPI_SUCCESS. Значение MPI_DUP_FN представляет собой простейшую функцию дублирования. Она возвращает значение флага flag = 1, значение атрибута в переменной attribute_val_out и код завершения MPI_SUCCESS.

    Аналогичной copy_fn является функция удаления, которая определяется следующим образом. Функция delete_fn вызывается, когда коммуникатор удаляется вызовом MPI_Comm_free или при вызове MPI_Attr_delete. Она должна иметь тип MPI_Delete_function, который определяется следующим образом:

    typedef int MPI_Delete_function(MPI_Comm comm,  int keyval, void *attribute_val, void *extra_state);
     SUBROUTINE DELETE_FUNCTION(COMM, KEYVAL, ATTRIBUTE VAL, EXTRA STATE, IERR)

    Эта функция вызывается подпрограммами MPI_Comm_free, MPI_Attr_delete и MPI_Attr_put. Функция удаления может быть "пустой" - MPI_NULL_DELETE_FN.

    Функция MPI_NULL_DELETE_FN не выполняет никаких действий, возвращая только значение MPI_SUCCESS.

    Специальное значение ключа MPI_KEYVAL_INVALID никогда не возвращается подпрограммой MPI_Keyval_create. Его можно использовать для инициализации ключей.

    Удаление ключа keyval

    int MPI_Keyval_free(int *keyval)
    
    MPI_KEYVAL_FREE(KEYVAL, IERR)

    Эта функция присваивает параметру keyval значение MPI_KEYVAL_INVALID. Используемый атрибут можно удалить, поскольку фактическое удаление происходит только после того, как будут удалены все ссылки на атрибут. Эти ссылки должны быть явным образом удалены программой, например, посредством вызова MPI_Attr_delete - каждый такой вызов удаляет один экземпляр атрибута, либо вызовом MPI_Comm_free, который удаляет все экземпляры атрибута, связанные с удаляемым коммуникатором.

    Задание атрибута attribute, который в дальнейшем может использоваться подпрограммой MPI_Attr_get

    int MPI_Attr_put(MPI_Comm comm, int keyval,  void* attribute)
    
    MPI_ATTR_PUT(COMM, KEYVAL, ATTRIBUTE, IERR)

    С атрибутом ассоциируется значение ключа keyval. Если значение атрибута уже задано, результат будет аналогичен ситуации, когда сначала для удаления предыдущего значения вызывается MPI_Attr_delete (и выполняется функция обратного вызова delete_fn ), а затем сохраняется новое значение. Вызов завершится с ошибкой, если нет ключа со значением keyval. В частности, MPI_KEYVAL_INVALID - ошибочное значение ключа. Не допускается изменение системных атрибутов MPI_TAG_UB, MPI_HOST, MPI_IO и MPI_WTIME_IS_GLOBAL. Если атрибут уже назначен, вызывается функция удаления, заданная при создании соответствующего ключа.

    Определение значения атрибута attribute, соответствующее значению ключа keyval

    int MPI_Attr_get(MPI_Comm comm, int keyval, void *attribute,  int *flag)
    
    MPI_ATTR_GET(COMM, KEYVAL, ATTRIBUTE, FLAG, IERR)

    Первый параметр задает коммуникатор, с которым связан атрибут. Если ключа со значением keyval нет, возникает ошибка. Ошибки не возникает, если значение key существует, но соответствующий атрибут не присоединен к коммуникатору comm. В этом случае возвращается значение флага flag = false.

    Вызов MPI_Attr_put передает в параметре attribute_val значение атрибута, а вызов подпрограммы MPI_Attr_get передает в параметре attribute_val адрес, по которому возвращается значение атрибута. Атрибуты должны извлекаться из программ, написанных на тех же языках, на которых они задавались с помощью вызова подпрограммы MPI_Attr_put.

    Удаление атрибута с указанным значением ключа

    int MPI_Attr_delete(MPI_Comm comm,  int keyval)
    
    MPI_ATTR_DELETE(COMM, KEYVAL, IERR)

    Делается это с помощью функции удаления атрибута delete_fn, заданной при создании keyval. Параметр comm задает коммуникатор, с которым связан атрибут. Все аргументы данной подпрограммы входные. При любом дублировании коммуникатора с помощью подпрограммы MPI_Comm_dup вызываются все функции копирования для атрибутов, установленных в данный момент времени. Порядок вызова произволен. Аналогичные действия выполняются при удалении коммуникатора вызовом MPI_Comm_free, но вызываются все функции удаления.

    Реализации MPI

    Существуют различные реализации MPI. Среди них MPICH ( MPI CHameleon, www.mcs.anl.gov) - свободно распространяемая реализация MPI с открытым кодом; LAM ( Local Area Multicomputer ) - еще одна реализация MPI (www.lam-mpi.org); Microsoft $$\text{\textregistered}$$ MPI и Intel $$\text{\textregistered}$$ MPI и т. д.

    Существуют реализации MPI, ориентированные на работу в среде грид.

    Новое в спецификации MPI-2

    В спецификации MPI-2 появились новые возможности, превратившие MPI в ещё более гибкий инструмент разработки параллельных программ.

    Вот краткий перечень новых (по сравнению с MPI-1 ) возможностей:

  • возможность запуска новых процессов во время выполнения MPI- программы;
  • односторонние двухточечные обмены;
  • параллельные операции ввода-вывода;
  • модифицированные привязки к языкам;
  • новые предопределённые типы данных;
  • расширенные возможности коллективных обменов;
  • внешние интерфейсы;
  • поддержка многопоточности и другие.
  • Возможность запуска новых процессов во время выполнения MPI-программы

    В MPI-1 параллельная программа запускается в определённом и фиксированном количестве процессов. Это не позволяет приложению, например, "подстраиваться" под изменяющуюся трудоёмкость расчёта. В то же время такой инструмент параллельного программирования как PVM поддерживает возможность динамического изменения числа процессов параллельного приложения. Эта возможность появилась в MPI-2.

    Дополнительный процесс (несколько процессов) может быть запущен во время выполнения программы. Процесс может быть также остановлен.

    Запуск одного процесса

    int MPI_Comm_spawn(char *command,  char *argv[], int maxprocs, MPI_Info info,  
      int root, MPI_Comm comm, MPI_Comm *intercomm,  int array_of_errcodes[])
    MPI_Comm_spawn(command, argv, maxprocs, info, root, comm, intercomm, array_of_errcodes,  ierror)

    Входные параметры:

  • command - командная строка запуска процесса;
  • argv - аргументы командной строки запуска процесса;
  • maxprocs - максимальное количество запускаемых процессов;
  • info - указывает системе как и где запускается процесс;
  • root - ранг главного процесса.
  • Выходные параметры:

  • intercomm - интеркоммуникатор между исходной группой процессов и вновь запущенными процессами;
  • array_of_errcodes - коды завершения для запущенных процессов.
  • При запуске группы процессов для них создаётся собственный коммуникатор MPI_COMM_WORLD, отличный от такого же для родительских процессов. Это коллективная операция. Она завершается после того, как во всех дочерних процессах состоится вызов MPI_Init. Завершение данного вызова в родительском процессе не означает, что в дочерних процессах завершены все вызовы MPI_Init.

    Интеркоммуникатор intercomm содержит родительский процесс в локальной группе и дочерние процессы.

    Командная строка запуска дочернего процесса представляет собой строку, содержащую имя исполняемого файла.

    Дочерний процесс обязательно должен вызывать MPI_Init, иначе результат не определён.

    Аргумент argv является массивом строковых значений, представляющих аргументы запускаемых программ.

    При вызове MPI_Comm_spawn предпринимается попытка запустить maxprocs процессов. Если какой-то процесс не может быть запущен, возвращается значение MPI_ERR_SPAWN.

    Аргумент info создаётся вызовом специальной подпрограммы MPI_Info_create.

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

    Опуская подробное описание её параметров, приведём интерфейс:

    Запуск нескольких процессов

    int MPI_Comm_spawn_multiple(int count, char *array_of_commands[], char **array_of_argv[], 
      int array_of_maxprocs[], MPI_Info array_of_info[], int root, 
      MPI_Comm comm, MPI_Comm *intercomm, int array_of_errcodes[])
    
    MPI Comm_spawn_multiple(count, array_of_commands, array_of_argv, 
      array_of_ maxprocs, array_of_info, root, comm, intercomm, array_of_errcodes, ierror)

    Подпрограмма MPI_Get_parent возвращает родительский интеркоммуникатор вызывающего процесса:

    Определение родительского интеркоммуникатора

    int MPI_Comm_get_parent(MPI_Comm *parent)
    
    MPI_Comm_get_parent(parent, ierr)

    Если данный процесс не является дочерним по отношению к какому-либо другому процессу, возвращается значение "пустого" коммуникатора MPI_COMM_NULL.

    Взаимодействие между группами процессов

    MPI-2 допускает организацию обмена сообщениями между группами процессов, которые запущены независимо друг от друга. Это позволяет, например, "подключиться" к параллельной программе приложению, выполняющему обработку данных. Данная возможность полезна при создании клиент-серверных приложений и в других ситуациях.

    Основные механизмы взаимодействия:

  • связь по имени;
  • связь через порт.
  • Программная реализация взаимодействия - в функциях:

  • MPI_Open_port
  • MPI_Close_port
  • MPI_Comm_accept
  • MPI_Publish_name
  • MPI_Unpublish_name
  • MPI_Lookup_name и других подпрограммах.
  • Односторонние обмены

    Односторонние обмены основаны на механизме удалённого доступа к памяти ( RMA -Remote Memory Access ) и позволяют процессу, инициировавшему обмен, самостоятельно задать параметры обмена как для источника, так и для адресата сообщения.

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

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

    В односторонних обменах эти функции разделены.

    Односторонний обмен возможен, если процесс создаёт "окно", доступное всем остальным процессам (рис. 3.13).

    Окно создаётся (коллективным) вызовом подпрограммы MPI_Win_create.

    Создание окна

    int MPI_Win_create(void *base, MPI_Aint size, int disp_unit, 
    MPI_Info info, MPI_Comm comm, MPI_Win *win)
    
    MPI_Win_create(base, size, disp unit, info, comm, win, ierror)

    Входные параметры:

  • base - адрес окна;
  • size - размер окна в байтах;
  • disp_unit - масштабный множитель для вычисления смещений;
  • info - информационный параметр;
  • comm - коммуникатор.
  • Выходной параметр - win - окно.

    (рис 3.13) Окно одностороннего обмена

    Аннулировать окно можно вызовом подпрограммы:

    int MPI_Win_free(MPI_Win *win) MPI_Win_free(win, ierror)

    Три операции одностороннего обмена являются неблокирующими операциями:

  • MPI_Put - передача данных от отправителя в окно;
  • MPI_Get - передача данных из окна отправителю;
  • MPI_Accumulate - обновление окна получателя.
  • Операция Put

    int MPI_Put(void *origin_addr,  int origin_count, MPI_Datatype origin_datatype,  
      int target_rank, MPI_Aint target_disp,  int target_count, 
      MPI_Datatype target_datatype, MPI_Win win)
    MPI_Put(origin_addr,  origin_count,  origin_datatype,  target_rank, 
    	target_disp, target_count, target_datatype, win,  ierror)

    Входные параметры:

  • origin_addr - адрес буфера отправки сообщения;
  • origin_count - количество элементов в буфере отправки;
  • origin_datatype - тип передаваемых данных;
  • target_rank - ранг адресата;
  • target_disp - смещение от начала окна приёма до буфера приёма;
  • target_count - количество принимаемых данных;
  • target_datatype - тип принимаемых данных;
  • win - окно приёма.
  • При выполнении этой операции данные размещаются в буфере приёма по адресу

    адрес_окна + смещение х disp_unit

    При вызове подпрограммы MPI_Get данные копируются в обратном направлении - из памяти адресата в память "источника".

    Коллективные операции

    В MPI-2 расширены возможности коллективных обменов сообщениями.

    Расширения заключаются в обобщении некоторых операций коллективного обмена на интеркоммуникаторы, введении дополнительных конструкторов интеркоммуникаторов, введении двух новых операций обмена - обобщённой операции "all-to-all" и операции исключающего сканирования. Есть и другие расширения.

    Подпрограмма MPI_Comm_create может использоваться для создания интеркоммуникаторов.

    Внешние интерфейсы

    Механизм внешних интерфейсов позволяет программисту добавить новую функциональность поверх базовой функциональности MPI.

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

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

    При использовании обобщённых запросов "ответственность" за выполнение операции берёт на себя приложение. Оно сообщает MPI о завершении операции.

    Операции с обобщёнными запросами:

  • MPI_Grequest_start
  • MPI_Grequest_complete
  • и некоторые другие.

    Другие возможности MPI-2

    Среди других возможностей MPI-2:

  • Операции декодирования производных типов.
  • Ассоциирование пользовательской информации с полями структуры status.
  • Присвоение имён объектам MPI (например, коммуникаторам, окнам и др.).
  • Многопоточность ( MPI_Init_thread, MPI_Thread_single, MPI_Thread_multiple и др.).
  • Новые операции кеширования атрибутов.
  • Параллельные операции ввода-вывода ( MPI_File_open, MPI_File_close, MPI_File_read, MPI_File_write и др.).
  • Лабораторная работа 0.1 Знакомство со структурой MPI-программы и процедурами блокирующего двухточечного обмена MPI

    Двухточечные блокирующие обмены

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

    Трансляция MPI-программ

    Утилиты трансляции и сборки находятся в каталоге /каталог_установки_МРI/bin.Его следует включить в путь поиска исполняемых файлов. Утилиты трансляции запускают трансляторы обычных языков C/C ++ и Fortran. В командную строку их запуска, при необходимости, подставляют ссылки на необходимые библиотечные и заголовочные файлы.

    Для трансляции и компоновки программ на языке C++ используется команда mpiCC, для трансляции программ на языке C - команда mpicc. Для трансляции и компоновки программ на языке Fortran 77 и Fortran 90 используются команды mpif77 и mpif90.

    Информацию о ключах можно найти на справочных страницах MPICH (для их просмотра применяют команду man ). Пример применения команды:

    mpicc -o fft fft.c

    Назначить другой транслятор можно, определив значения переменных окружения MPICH_CC, MPICH_F77, MPICH_CCC или MPICH_F90. Изменить программу-компоновщик можно с помощью переменных окружения MPICH_CLINKER, MPICH_F77LINKER, MPICH_CCLINKER и MPICH_F9 0LINKER.

    Выполнение MPI-программ

    Для выполнения MPI -программ в MPICH используется загрузчик приложений mpirun. Он запускает указанное количество копий программы. Команда запуска:

    mpirun -np n [ключи MPI] программа[ключи и аргументы программы]

    где n - число запускаемых процессов. Некоторые ключи MPI (они указываются перед именем исполняемого файла программы) приведены в таблице:

    Ключ Описание
    - h Краткая информация о команде
    - machinefile файл Для запуска программы использовать список компьютеров из указанного файла

    Лабораторная работа

    В заданиях лабораторной работы 0.1 предлагается дописать или исправить предлагаемые фрагменты программ на языках Fortran и C, написанные с использованием процедур MPICH 1.2.7, в том числе, процедуры блокирующего двухточечного обмена. Пропущенные фрагменты обозначены многоточием.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 27 - 29 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

    В исходном тексте программы на языке Fortran пропущены вызовы процедур подключения к MPI, определения количества процессов и ранга процесса. Добавить эти вызовы, откомпилировать и запустить программу.

    Program main_mpi include 'mpif.h' 
    integer myid, numprocs, ierr
    …
    print *, "
    process", myid, "of", numprocs
    call mpi_finalize(ierr)
    stop
    end

    Задание 2

    В исходном тексте программы на языке C пропущены вызовы процедур подключения к MPI, определения количества процессов и ранга процесса. Добавить эти вызовы, откомпилировать и запустить программу.

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myid,  numprocs;
    
      fprintf(stdout,"Process %d of %d\n",  myid,  numprocs);
      MPI_Finalize();
      return 0;
    }

    Задание 3

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myid,  numprocs; 
      char message[20]; 
      int myrank; 
      MPI_Status status; 
      int TAG = 0; 
    MPI_Init(argc, argv);
    MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
    if   (myrank == 0)
    {
      strcpy(message, "Hi, Second Processor!"); 
      MPI_Send(...);
    }
      else
    {
      MPI_Recv(...);
      printf("received:  %s\n",  message);
    }
      MPI_Finalize(); 
      return 0;
    }

    Задание 4

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myrank,  size,  message; int TAG = 0; 
      MPI_Status status; 
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank);
      MPI_Comm_size(MPI_COMM_WORLD, size);
      message = myrank;
      if((myrank % 2) == 0)
    {
      if((myrank + 1) != size) MPI_Send(...);
    }
      else
    {
      if(myrank  != 0) MPI_Recv(...);
      printf("received :%i\n",  message);
    }
      MPI_Finalize(); return 0;
    }

    Задание 5

    Следующая программа на языке Fortran написана некорректно. Найдите ошибку и исправьте ее.

    program main_mpi include  'mpif.h'
    integer rank,  tag,  cnt,  ierr,  status(mpi_status_size) 
    real sndbuf,  rcvbuf tag = 0
    sndbuf = 3.14159 cnt = 1
    call mpi_init(ierr)
    call mpi_comm_rank(mpi_comm_world, rank, ierr) 
    if (rank.eq.0)   then
    call mpi_recv(rcvbuf, cnt, mpi_real, 1, tag, mpi_comm_world, status, ierr)
    call mpi_send(sndbuf, cnt, mpi_real, 1, tag, mpi_comm_world, ierr)
    else
    call mpi_recv(rcvbuf, cnt, mpi_real, 0, tag, mpi_comm_world, status, ierr)
    call mpi_send(sndbuf, cnt, mpi_real, 0, tag, mpi_comm_world, ierr)
    end if
    call mpi_finalize(ierr)
    stop
    end

    Лабораторная работа 0.2 Знакомство с процедурами буферизованного и неблокирующегодвухточечного обмена MPI

    Двухточечный буферизованный обмен

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

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

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

    Двухточечные неблокирующие обмены

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

    Неблокирующий обмен выполняется в два этапа:

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

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

    Подпрограмма MPI_Wait блокирует работу процесса до завершения приема или передачи сообщения. Функции MPI_Wait и MPI_Test можно использовать для завершения операций приема и передачи.

    Подпрограммы-пробники

    Получить информацию о сообщении до его помещения в буфер приема можно с помощью подпрограмм-пробников MPI_Probe и MPI_IProbe. На основании полученной информации принимается решение о дальнейших действиях. С помощью вызова подпрограммы MPI_Probe фиксируется поступление (но не прием!) сообщения. Затем определяется источник сообщения, его длина, выделяется буфер подходящего размера и выполняется прием сообщения.

    Лабораторная работа

    В заданиях лабораторной работы 0.2 предлагается дописать предлагаемые фрагменты программ на языках Fortran и C, написанные с использованием процедур MPICH 1.2.7. Пропущенные фрагменты обозначены многоточием.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 30 - 34 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int *buffer; 
      int myrank; 
      MPI_Status status; 
      int buffsize = 1; 
      int TAG = 0; 
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
      if (myrank == 0)
    {
      buffer = (int *) malloc(buffsize + MPI_BSEND_OVERHEAD); 
      buffer = (int *) 10;
    }
      else
    {
      MPI_Recv(buffer,  buffsize,  MPI_INT, 0, TAG, MPI_COMM_WORLD,
    status); 
      printf("received:  %i\n",  buffer);
    }
      MPI_Finalize(); return 0;
    }

    Задание 2

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

    program main_mpi include  'mpif.h'
    integer rank, tag, cnt, ierr, status(mpi_status_size) integer request
    real sndbuf(5) /1., 2., 3., 4., 5./ real rcvbuf(5) 
    cnt = 5 
    tag = 0
    call mpi_init(ierr)
    call mpi_comm_rank(mpi_comm_world, rank, ierr) if(rank.eq.0) then
    ...
    print  *, "process  ", rank, " send before wait", sndbuf
    ...
    print  *, "process  ", rank, " send after wait", sndbuf else
    ...
    print  *, "process  ", rank, " received before wait", rcvbuf
    ...
    print  *, "process  ", rank, " received after wait", rcvbuf 
    end if
    call mpi_finalize(ierr)
    stop
    end

    Задание 3

    В исходном тексте программы на языке C пропущены вызовы подпрограмм-пробников. Добавить эти вызовы, откомпилировать и запустить программу.

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myid, numprocs, **buf, source, i;
      int message[3]  = {0, 1, 2};
      int myrank, data = 2002, count, TAG = 0;
      MPI_Status status;
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank);
      if(myrank == 0)
    {
      MPI_Send(data, 1, MPI_INT, 2, TAG, MPI_COMM_WORLD);
    }
      else if (myrank == 1)  
    {
      MPI_Send(message, 3, MPI_INT, 2, TAG, MPI_COMM_WORLD);
    }
      else
    {
    ...
    source = status.MPI_SOURCE;
    MPI_Get_count (...) ;
    for (i = 0;  i < count;	
    {
      buf[i]  =  (int *)malloc(count*sizeof(int));
    }
      MPI_Recv(buf[0], count, MPI_INT, source, TAG, MPI_COMM_WORLD, status);
      for  (i = 0;  i < count;  i++)
       { printf("received:  %d\n",  buf[i]);
       }
    }
    MPI_Finalize(); 
    return 0;
    }

    Задание 4

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

    program main_mpi include  'mpif.h'
    integer rank, i, k, ierr, tag,dest,... 
    real x 
    tag = 0 
    dest = 2
    call mpi_init(ierr)
    call mpi_comm_rank(mpi_comm_world,  rank,   ierr) if (rank.eq.0)   
      then i = 2002
    call mpi_send(i, 1, mpi_integer, dest, tag, mpi_comm_world, ierr)
    else if(rank.eq.l)   then 
    x = 3.14159
    call mpi_send(x, 1, mpi_real, dest, tag, mpi_comm_world, ierr) 
    else
    do k = 1, 2
    ...
      if   (status(mpi_source).eq.0)   then
     call mpi_recv(i, 1, mpi_integer, 0, tag, mpi_comm_world, status,  ierr)
      print *, "received ", i, "  from 0" else
    call mpi_recv(x, 1, mpi_real, 1, tag, mpi_comm_world, status, ierr)
      print *, "received ", x, "  from 1"
      end if 
    end do 
    end if
    call mpi_finalize(ierr) 
    end

    Задание 5

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

    program main_mpi
    
    integer rank, tagl, tag2, cnt, ierr, ...
    integer request
    real sndbuf1, sndbuf2, rcvbuf1, rcvbuf2
    cnt = l tag = 0
    sndbuf1 = 3.14159 sndbuf2 = 2.71828
    ...
    call mpi_comm_rank(mpi_comm_world,  rank,   ierr) if (rank.eq.0) then
    call mpi_ssend(sndbuf1, cnt, mpi_real, 1, tag1, mpi_comm_world, ierr)
    print *, "process ", rank, " send ",  sndbuf1
    call mpi_send(sndbuf2, cnt, mpi_real, 1, tag2, mpi_comm_world, ierr)
    print *, "process ", rank, " send ",   sndbuf2 
    else
    call mpi_irecv(rcvbuf1,cnt, mpi_real, 0, tag1, mpi_comm_world, request, ierr)
    call mpi_recv(rcvbuf2, cnt, mpi_real, 0, tag2, mpi_comm_world, status,   ierr)
    print *, "process ", rank, " received before wait", rcvbuf1 
    print *, "process ", rank, " received before wait",  rcvbuf2
    ...
    print *,"process ", rank, " received after wait", rcvbuf1 
    print *, "process ", rank, " received after wait", rcvbuf2 
    end if
    ...
    end

    Лабораторная работа 0.3 Знакомство с процедурами коллективного обмена MPI

    Коллективные обмены

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

    Коллективные обмены характеризуются следующим:

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

    Широковещательная рассылка

    Широковещательная рассылка выполняется выделенным процессом, который называется главным ( root ). Все остальные процессы, принимающие участие в обмене, получают по одной копии сообщения от главного процесса (рис. 6.1).

    (рис 6.1) Широковещательная рассылка

    Выполняется широковещательная рассылка с помощью подпрограммы MPI_Bcast (см. Методическое пособие).

    Операции редукции

    Операции редукции относятся к категории глобальных вычислений. В глобальной операции приведения к данным от всех процессов из заданного коммуникатора применяется операция MPI_Reduce (рис. 6.2).

    (рис 6.2) Глобальная операция приведения

    Аргументом операции приведения является массив данных - по одному элементу от каждого процесса. Результат такой операции - единственное значение.

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

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

    Коммуникаторы бывают двух типов: интракоммуникаторы - для операций внутри одной группы процессов и интеркоммуникаторы - для двухточечного обмена между двумя группами процессов.

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

    Созданию нового коммуникатора предшествует создание соответствующей группы процессов. Операции создания групп аналогичны математическим операциям над множествами:

  • объединение - к процессам первой группы добавляются процессы второй группы, не принадлежащие первой;
  • пересечение - в новую группу включаются все процессы, принадлежащие двум группам одновременно. Ранги им назначаются как в первой группе;
  • разность - в новую группу включаются все процессы первой группы, не входящие во вторую группу. Ранги назначаются как в первой группе.
  • Новую группу можно создать только из уже существующих групп. Базовая группа, из которой формируются все другие группы, связана с коммуникатором MPI_COMM_WORLD.

    Доступ к группе group, связанной с коммуникатором comm можно получить, обратившись к подпрограмме MPI_Comm_group.

    В MPI имеются подпрограммы-конструкторы новых групп (см. Методическое пособие). Есть и деструктор - MPI_Group_free.

    Создание коммуникатора - коллективная операция и соответствующая подпрограмма должна вызываться всеми процессами коммуникатора. Подпрограмма MPI_Comm_dup дублирует уже существующий коммуникатор.

    Подпрограмма MPI_Comm_create создает новый коммуникатор из подмножества процессов другого коммуникатора.

    Вызов этой подпрограммы должны выполнить все процессы из старого коммуникатора, даже если они не входят в группу group, с одинаковыми аргументами. Данная операция применяется только к интракоммуникаторам. Она позволяет выделять подмножества процессов со своими областями взаимодействия, если, например, требуется уменьшить "зернистость" параллельной программы.

    Лабораторная работа

    В заданиях лабораторной работы 0.3 предлагается дописать предлагаемые фрагменты программ на языке C, написанные с использованием процедур MPICH 1.2.7. Пропущенные фрагменты обозначены многоточием.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 37 - 49 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      char data[24]; 
      int myrank, count = 25; 
      MPI_Status status; 
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
      if (myrank == 0)
    {
      strcpy(data, "Hi, Parallel Programmer!"); 
      ...
      printf("send: %s\n", data);
    }
      else
    {
    ...
    printf("received: %s\n", data);
    }
    MPI_Finalize(); return 0;
    }

    Задание 2

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc, char *argv[])
    {
      int myrank; 
      int root = 0; int count = 1; float a, b; 
      int n;
    
      MPI_Init(argc, argv);
      MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
      if (myrank == 0)
    {
      printf("Enter a, b, n\n");
      scanf("%f %f %i", a, b, n);
    …
    }
      else
    {
    …
      printf("%i Process got %f %f %i\n", myrank, a, b, n);
    }
      MPI_Finalize(); return 0;
    }

    Задание 3

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      char message[24];
      MPI_Group MPI_GROUP_WORLD;
      MPI_Group group; 
      MPI_Comm fcomm; 
      int size, q, proc; 
      int* process_ranks; 
      int rank, rank_in_group;
    
      MPI_Status status; 
      MPI_Init(argc, argv);
      MPI_Comm_size(MPI_COMM_WORLD, size); 
      MPI_Comm_rank(MPI_COMM_WORLD, rank);
      printf("New group contains processes:"); 
      q = size - 1;
      process_ranks = (int*) malloc(q*sizeof(int)); 
      for (proc = 0; proc < q; proc++)
    {
      process_ranks[proc]   = proc; 
      printf("%i ", process_ranks[proc]);
    }
      printf("\n");
    ...
      if (fcomm != MPI_COMM_NULL)  
    {
      MPI_Comm_group(group, fcomm); 
      MPI_Comm_rank(fcomm, rank_in_group); 
      if (rank_in_group == 0)   
    {
      strcpy(message, "Hi, Parallel Programmer!"); 
      MPI_Bcast(message, 25, MPI_BYTE, 0, fcomm); 
      printf("0 send: %s\n", message);
    }
      else
    {
      MPI_Bcast(message, 25, MPI_BYTE, 0, fcomm); 
      printf("%i received: %s\n", rank_in_group, message);
    }
      MPI_Comm_free(fcomm); 
      MPI_Group_free(group);
    }
      MPI_Finalize(); return 0;
    }

    Задание 4

    В программе на языке C сначала создается подгруппа, состоящая из процессов с рангами 1, 3, 5 и 7, и соответствующий ей коммуникатор. Затем выполняется редукция (суммирование) по процессам, входящим в новую группу. Вызов подпрограммы редукции и некоторые другие важные фрагменты пропущены. Добавить эти вызовы, откомпилировать и запустить программу.

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      int myrank, i;
      int count = 5,  root = 1;
      MPI_Group MPI_GROUP_WORLD, subgroup;
      int ranks[4] = {1, 3, 5, 7};
      MPI_Comm subcomm;
      int sendbuf[5] = {1, 2, 3, 4, 5}; 
      int recvbuf[5];
    
      MPI_Init(argc, argv);
      MPI_Comm_group(MPI_COMM_WORLD, MPI_GROUP_WORLD); 
      MPI_Group_incl(MPI_GROUP_WORLD, 4, ranks, subgroup); 
      MPI_Group_rank(subgroup, myrank);
      ...
      if(myrank != MPI_UNDEFINED)
    {
      MPI_Reduce(sendbuf, recvbuf, count, MPI_INT, MPI_SUM, root, subcomm);
    
      if(myrank == root)   
        { printf("Reduced values"); 
          for(i = 0;  i < count;  i++)
       { printf(" %i  ", recvbuf[i]);}
        }
      printf("\n");
    
      MPI_Comm_free(subcomm);
      MPI_Group_free(MPI_GROUP_WORLD);
      ...
    }
      MPI_Finalize(); return 0;
    }

    Лабораторная работа 0.4 Производные типы в MPI

    Производные типы

    Производные типы данных создаются во время выполнения программы. Создание типа - двухступенчатый процесс, который состоит из двух шагов:

  • конструирование типа;
  • регистрация типа.
  • После завершения работы с производным типом, он аннулируется. При этом все производные от него типы остаются и могут использоваться дальше, пока и они не будут уничтожены. Последовательность удаления может быть любой.

    Производные типы данных создаются из базовых типов с помощью подпрограмм-конструкторов. Операции создания производных типов могут применяться рекурсивно.

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

    Подпрограмма MPI_Type_struct (см. Методическое пособие) является наиболее общим конструктором типа в MPI - программист может использовать полное описание каждого элемента типа. Если пересылаемые данные содержат подмножество элементов массива, такая детальная информация не нужна, поскольку у всех элементов один и тот же базовый тип. MPI содержит три конструктора, которые можно использовать в такой ситуации: MPI_Type_contiguous, MPI_Type_vector и MPI_Type_indexed. Первый из них создает производный тип, элементы которого являются непрерывно расположенными элементами массива. Второй создает тип, элементы которого расположены на одинаковых расстояниях друг от друга, а третий создает тип, содержащий произвольные элементы.

    "Векторный" тип создается конструктором MPI_Type_vector. Схема расположения данных в новом типе представлена на рис. 6.3.

    (рис 6.3) Схема векторного типа данных

    Лабораторная работа

    В заданиях лабораторной работы 0.4 предлагается дописать предлагаемые фрагменты программ на языке C, написанные с использованием процедур MPICH 1.2.7. Пропущенные фрагменты обозначены многоточием.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 55 - 60 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

    В программе на языке Fortran имеется трехмерный массив arr. Используя приведенный ниже шаблон, дополните программу таким образом, чтобы в ней определялся производный тип, соответствующий: а) горизонтальному сечению массива; б) вертикальному сечению массива. Затем сечение должно пересылаться от процесса с рангом 0 процессу с рангом 1. Откомпилировать и запустить программу.

    program main_mpi
    include  'mpif.h'
    parameter  (n = 50)
    real arr(n, n, n), b(...)
    integer slice, sizeofreal
    integer rank, ierr, status(MPI_STATUS_SIZE)
    integer tag, cnt, vcount, blocklen, stride, count
    cnt = ...
    tag = 0
    vcount = ...
    blocklen = ...
    call MPI_Init(ierr)
    call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr) 
    if (rank.eq.0)   then
    call MPI_Type_extent(MPI_REAL, sizeofreal, ierr) stride = ...
    call MPI_Type_vector(...)
    call MPI_Type_commit(slice, ierr)
    call MPI_Send(arr (...),  cnt,  slice, 1, tag, MPI_COMM_WORLD, ierr) 
      else if (rank.eq.1)   
      then count = ...
    call MPI_Recv(b, count,  MPI_REAL, 0, tag, MPI_COMM_WORLD, status,  ierr) 
    print *,  b 
    end if
    call MPI_Finalize(ierr) end

    Задание 2

    В программе на языке C задаются типы членов производного типа, затем количество элементов каждого типа. После этого вычисляются адреса членов типа indata и определяются смещения трех членов производного типа относительно адреса первого, для которого смещение равно 0. Затем определяется производный тип. Аргументы подпрограмм MPI_Type_struct и MPI_Type_commit, а также некоторые другие фрагменты пропущены. Добавить эти фрагменты, откомпилировать и запустить программу.

    #include "mpi.h" 
    #include <stdio.h> 
    struct newtype  
    {
      Float a; float b; int n;
    };
    
    int main(int argc,char *argv[])
    {
      int myrank;
      MPI_Datatype NEW_MESSAGE_TYPE; 
      int block_lengths[3] ; 
      MPI_Aint displacements[3]; 
      MPI_Aint addresses[4]; 
      MPI_Datatype typelist[3]; 
      int blocks_number; 
      struct newtype indata; 
      int tag = 0; 
      MPI_Status status;
      ...
      MPI_Comm_rank(MPI_COMM_WORLD, myrank); 
      typelist[0] = MPI_FLOAT;
      typelist[1] = MPI_FLOAT; 
      typelist[2] = MPI_INT;
      block_lengths[0] = block_lengths[1] = block_lengths[2] = 1; 
      MPI_Address(indata, addresses[0]); 
      MPI_Address((indata.a), addresses[1]); 
      MPI_Address((indata.b), addresses[2]); 
      MPI_Address((indata.n), addresses[3]); 
      displacements[0]  = addresses[1] - addresses[0]; 
      displacements[1]  = addresses[2] - addresses[0]; 
      displacements[2]  = addresses[3] - addresses[0]; 
      blocks_number = 3; 
      MPI_Type_struct(...); 
      MPI_Type_commit(...);
    
      if   (myrank == 0)
    {
      indata.a = 3.14159; 
      indata.b = 2.71828; 
      indata.n = 2002;
    
      MPI_Send(indata, 1,NEW_MESSAGE_TYPE, 1, tag, MPI_COMM_WORLD);
      printf("Process  %i  send:  %f  %f  %i\n", 
      myrank,  indata.a, indata.b, indata.n); 
    }
      else {
      MPI_Recv(indata, 1, NEW_MESSAGE_TYPE, 0, 
        tag, MPI_COMM_WORLD, status);
      printf("Process %i received: %f %f %i, status %s\n", 
        myrank, indata.a,  indata.b,  indata.n,  status.MPI_ERROR);
    }
      ...
      MPI_Finalize(); return 0;
    }

    Лабораторная работа 0.5 Виртуальные топологии в MPI

    Виртуальные топологии

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

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

    В MPI существуют два типа топологии:

  • декартова топология - прямоугольная решетка произвольной размерности (рис. 6.4);
  • топология графа.
  • Над топологиями можно выполнять различные операции. Декартовы решетки можно расщеплять на гиперплоскости, удаляя некоторые измерения. Данные можно сдвигать вдоль выбранного измерения декартовой решетки.

    Сдвигом называют пересылку данных между процессами вдоль определенного измерения. Вдоль избранного измерения могут быть организованы коллективные обмены.

    Для того, чтобы связать структуру декартовой решетки с коммуникатором MPl_COMM_WORLD, необходимо задать следующие параметры:

  • размерность решетки (значение 2 соответствует плоской, двумерной решетке);
  • размер решетки вдоль каждого измерения (размеры {10, 15}, например, соответствуют плоской прямоугольной решетке, протяженность которой вдоль оси x составляет 10 узлов-процессов, а вдоль оси y - 15 узлов);
  • периодичность вдоль каждого измерения (решетка может быть периодической, если процессы, находящиеся на противоположных концах ряда, взаимодействуют между собой).
  • MPI дает возможность системе оптимизировать отображение виртуальной топологии процессов на физическую с помощью изменения порядка нумерации процессов в группе.

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

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

    (рис 6.4) Декартова топология

    Обратным действием по отношению к MPI_Cart_coords обладает подпрограмма MPI_Cart_rank. С ее помощью можно определить ранг процесса по его декартовым координатам в соответствующем коммуникаторе.

    Между процессами, организованными в декартову решетку, могут выполняться обмены особого вида. Это сдвиги, о которых мы уже упоминали. Имеются два типа сдвигов данных по группе из N процессов:

  • циклический сдвиг на J позиций вдоль ребра решетки. Данные от процесса K пересылаются процессу с номером (J + K) mod N ;
  • линейный сдвиг на J позиций вдоль ребра решетки, когда данные в процессе K пересылаются процессу с номером J + K,если ранг адресата находится в пределах между 0 и N.
  • Лабораторная работа

    В заданиях лабораторной работы 0.5 предлагается дописать и модифицировать предлагаемый фрагмент программы на языке C, написанный с использованием процедур MPI.

    Необходимый для выполнения данной лабораторной работы справочный материал можно найти на стр. 51 - 55 методического пособия "Средства программирования для многопроцессорных вычислительных систем".

    Задание 1

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

    #include "mpi.h"
    #include <stdio.h>
    int main(int argc,char *argv[])
    {
      MPI_Comm grid_comm; int dims[2]; 
      int periodic[2];
      int reorder = 1, q = 5, ndims = 2, maxdims = 2; 
      int coordinates[2]; 
      int my_grid_rank; 
      int coords[2]; 
      MPI_Comm row_comm;
    
      dims[0] = dims[1] = q; 
      periodic[0] = periodic[1] = 1; 
      coords[0] = 0;  
      coords[1] = 1; 
      MPI_Init(argc,  argv);
    
      MPI_Cart_create(MPI_COMM_WORLD, ndims, dims, periodic, reorder, grid_comm);
    
      printf("Process rank %i has coordinates %i %i\n", 
        my_grid_rank, coordinates[0],  coordinates[1]);
    
      MPI_Finalize(); return 0;
    }

    Задание 2

    Добавьте в программу из Задания 1 операции циклического и/или линейного сдвига вдоль определенного измерения.

    Задание 3

    Измените программу из Задания 1, наделив коммуникатор топологией некоторого графа. Детали обсудите с преподавателем.

    Вернуться к учебному плану