Пособие по MPI (реферат) (2) (Пособие по MPI (реферат) (2))

Посмотреть архив целиком














Реферат на тему «Пособие по MPI»





Подготовил Абрамов Андрей А-05-03
































26 ноября 2007 г.

  1. MPI_Init(), MPI_Finalize().

Функция MPI_Init() инициализирует параллельные вычисления. В качестве входных параметров используются &argc, &argv из функции main.


MPI_Finalize() завершает выполнение программы MPI.


int MPI_Initialized(int nFlag) показывает, была ли выполнена функция MPI_Init: если nFlag не нуль, то была, иначе – нет.


Далее идет текст программы, иллюстрирующий работу этих функций.

int _tmain(int argc, _TCHAR* argv[])

{

int nRank = 0;

int nInitialized = 0;

int nRes = 0;

MPI_Init(&argc, &argv);

MPI_Comm_rank(MPI_COMM_WORLD, &nRank);

printf(" Hello, this is proces number %d\n", nRank);

nRes = MPI_Initialized(&nInitialized);

if(nRes)

{

printf(" MPI_Initialized returned error \n");

}

else

{

if(nInitialized)

{

printf(" MPI was initialized\n");

}

else

{

printf(" MPI was not initialized is \n");

}

}

MPI_Finalize();

return 0;

}


Результат работы данной программы на 3-х процессах:


Hello, this is proces number 1

MPI was initialized

Hello, this is proces number 2

MPI was initialized

Hello, this is proces number 0

MPI was initialized


  1. Пример на взаимную блокировку MPI_Send/MPI_Recv.


Функция MPI_Send().

int nError MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)

  • buf - начало буфера, содержащего данные, которые будут отосланы. Для C это - адрес.

  • count - число элементов, которые будут отосланы (не байт)

  • datatype - тип данных

  • dest - ранг процесса, места назначения, для сообщения

  • tag - произвольное число, которое можно использовать для отличия от других сообщений

  • comm - (определенный) коммуникатор

  • (nError - возвращаемый функцией код ошибки).

Функция MPI_Send является блокирующей, т.е. управление не вернется программе, пока функция не завершится.

Функция MPI_Recv().

int nError MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)

Параметры:

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

  • count - число элементов (не байт) в вашем буфере получателя

  • datatype - тип данных

  • source - ранг процесса, от которого данные будут приняты
    (Он может быть любым при задании параметром MPI_ANY_SOURCE)

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

  • comm - (определенный) коммуникатор

  • status - массив или структура возвращаемой информации.

  • (nError - возвращаемый функцией код ошибки).

Пример взаимной блокировки этих двух функций:

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











Каждый процесс содержит следующий код:

for(i=0; i

{

MPI_Send(…);

}

for(i=0; i

{

MPI_Recv (…);

}

Если запустить эти процессы, мы получим дедлок(тупик), поскольку оба процесса зайдут в функцию MPI_Send() и будут ждать ее завершения. А для ее завершения необходимо выполнение функции MPI_Recv, которая никогда не выполнится.

Следующий способ позволяет избежать дедлока:

Поменяем местами циклы в процессах 2 и 4 (процессы будут располагаться в шахматном порядке):

for(i=0; i

{

MPI_Recv (…);

}

for(i=0; i

{

MPI_Send(…);

}

Таким образом, они будут сначала принимать данные, а потом передавать. В то же время процессы 1,2 будут действовать наоборот. В этом случае дедлока не возникнет.

3) Использование функций MPI_Wait(), MPI_Test(), MPI_Probe().

Функция MPI_Wait – функция ожидания завершения неблокирующей операции. Int nError MPI_Wait(MPI_Request *request, MPI_Status *status) nError – возвращаемый функцией код ошибки. status – информация о завершенной операции. Request – запрос обмена. Это блокирующая операция. Возврат происходит после завершения операции, связанной с запросом request. В параметре status возвращается информация о законченной операции.

MPI_Test - функция проверки завершения неблокирующей операции. Int nError MPI_Test(MPI_Request *request, int *flag, MPI_Status *status) nError – возвращаемый функцией код ошибки. status – атрибуты сообщения, если операция завершилась. Request – запрос обмена. Flag – признак завершенности проверяемой операции. Это неблокирующая операция. Если связанная с запросом request операция завершена, возвращается flag = true, а status содержит информацию о завершенной операции. Если проверяемая операция не завершена, возвращается flag = false, а значение status в этом случае не определено.

int nError MPI_Probe(int source, int tag, MPI_Comm comm, int *flag, MPI_Status *status) source - ранг процесса, от которого данные будут приняты tag - произвольное число, которое можно использовать для отличия от других сообщений comm - (определенный) коммуникатор status - массив или структура возвращаемой информации. flag – удовлетворяет ли сообщение всем требованиям nError - возвращаемый функцией код ошибки.


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

Пример программы, которая использует перечисленные функции. Все процессы, номера которых кратны 2-м рассылают целочисленный буфер процессу с номером на 1 большим, чем у них. Затем проверяем, завершилась ли передача и выводим сообщение. На принимающей стороне проверяем, соответствует ли сообщение заданным параметрам, после этого принимаем. Причем, используем блокирующую операцию MPI_Wait для того, чтобы узнать, что прием закончен и тогда выводим на экран принятые значения с помощью функции mesParam:

// second_test.cpp : Defines the entry point for the console application.

//


#include "stdafx.h"

#include "mpi.h"



void mesParam(MPI_Status aStatus)

{

printf("Message parameters: \n");

if(aStatus.cancelled)

{

printf("Cancelled \n");

}

else

{

printf("Not cancelled \n");

}

printf("Error: %d\n", aStatus.MPI_ERROR);

printf("Parameters count: %d\n", aStatus.count);

printf("Source: %d\n", aStatus.MPI_SOURCE);

printf("Tag: \n", aStatus.MPI_TAG);

}


int _tmain(int argc, _TCHAR* argv[])

{

int nRank = 0;

int nSize = 0;

MPI_Init(&argc, &argv);

MPI_Comm_rank(MPI_COMM_WORLD, &nRank);

MPI_Comm_size(MPI_COMM_WORLD, &nSize);

if(nSize % 2 == 0)

{

if(nRank % 2 == 0)

{

int nBuf = 100;

int nRes = 0;

int nTestRes = 0;

int nFlag = 0;

MPI_Request request = MPI_REQUEST_NULL;

MPI_Status* status = MPI_STATUS_IGNORE;

// Асинхронная передача

nRes = MPI_Isend(&nBuf, 1, MPI_INT, nRank + 1, nRank, MPI_COMM_WORLD, &request);

if(nRes)

{

printf("MPI_Isend returned error %d\n", nRes);

}

//----------------------------------------MPI_Test-----------------------------------------

// Проверим, что передача завершилась

nTestRes = MPI_Test(&request, &nFlag, status);

if(nTestRes)

{

printf("MPI_Test returned error %d\n", nTestRes);

}

else

{

if(nFlag)

{

printf("MPI_Isend completed \n");

// Если nFlag==true выведем все параметры сообщения

mesParam(*status);

}

}

}

else

{

int nRes = 0;

int nBuf = 0;

int nFlag = 1;

int nProbeRes = 0;

MPI_Request request;

MPI_Status status;

status.cancelled = 0;

status.count = 1;

status.MPI_ERROR = 0;

status.MPI_SOURCE = nRank;

status.MPI_TAG = nRank;

//----------------------------------------MPI_Probe----------------------------------------

// Проверим, что сообщение удовлетворяет нашим требованиям

nProbeRes = MPI_Probe(nRank - 1, nRank - 1, MPI_COMM_WORLD, &nFlag, &status);

if(nProbeRes)

{

printf("MPI_Probe returned error %d\n", nProbeRes);

}

else

{

if(nFlag)

{

int nWaitResult = 0;

printf("Message matches requirements \n");

// Асинхронный прием

nRes = MPI_Irecv(&nBuf, 1, MPI_INT, nRank - 1, nRank - 1, MPI_COMM_WORLD, &request);

if(nRes)

{

printf("MPI_Irecv returned error %d\n", nRes);

}

//----------------------------------------MPI_Wait-------------------------------------

// Если удовлетворяет, подождем пока закончиться прием данных

nWaitResult = MPI_Wait(&request, &status);

if(nWaitResult)

{

printf("MPI_Wait returned error %d\n", nWaitResult);

}

else

{

// Если прием нормально завершился, выведем параметры сообщения на экран

mesParam(status);

}

}

}

}

}

MPI_Finalize();

return 0;

}

Результат работы программы для 2-х процессов:


Message matches requirements

Message parameters:

Not cancelled

Error: 0

Parameters count: 4

Source: 0

Tag: 0


  1. Функции MPI_Gather, MPI_Scatter

Функция MPI_Gather(). При выполнении этой функции все процессы рассылают данные корневому процессу (включая сам корневой).

int nError MPI_Gather(void* sendbuf, int sendcount,
MPI_Datatype sendtype, void* recvbuf, int recvcount,
MPI_Datatype recvtype, int root, MPI_Comm comm);

sendbuf начальный адрес буфера процесса-отправителя sendcount количество элементов в отсылаемом сообщении (целое) sendtype тип элементов в отсылаемом сообщении recvbuf начальный адрес буфера процесса сборки данных (существенно только для корневого процесса) recvcount количество элементов в принимаемом сообщении (целое, имеет значение только для корневого процесса) recvtype тип данных элементов в буфере процесса-получателя root номер процесса-получателя (целое) comm коммуникатор root – номер корневого процесса nError – возвращаемое значение ошибки.

Пример на функцию MPI_Gather(). Все процессы создают переменную типа int и передают ее корневому (нулевому), который сохраняет их в массив, затем выводит на экран:

int _tmain(int argc, _TCHAR* argv[])

{

int nError = 0;

int nSize = 0;

int nRank = 0;

int* pReadBuf = NULL;

int i=0;

MPI_Init(&argc, &argv);

MPI_Comm_size(MPI_COMM_WORLD, &nSize);

MPI_Comm_rank(MPI_COMM_WORLD, &nRank);

if(nRank == 0)

{

pReadBuf = (int *)malloc(nSize*sizeof(int));

}

nError = MPI_Gather(&nRank, 1, MPI_INT, pReadBuf, 1, MPI_INT, 0, MPI_COMM_WORLD);

if(nRank == 0)

{

printf("Принятые элементы массива: \n");

for(i=0; i

{

printf("%d\n", pReadBuf[i]);

}

}

MPI_Finalize();

return 0;

}

Результат работы функции для 4-х процессов:

Принятые элементы массива:

0

1

2

3

Функция MPI_Scatter(). Эта операция обратна MPI_Gather(), т.е. корневой процесс рассылает данные всем остальным.

int MPI_Scatter(void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm)

sendbuf - начальный адрес буфера процесса-отправителя(используется только корневым процессом) sendcount количество элементов в отсылаемом сообщении (целое, используется только корневым процессом) sendtype тип элементов в отсылаемом сообщении(используется только корневым процессом) recvbuf начальный адрес буфера процесса сборки данных recvcount количество элементов в принимаемом сообщении recvcount - количество элементов recvtype тип данных элементов в буфере процесса-получателя root номер процесса-получателя (целое) root – номер процесса-получателя comm коммуникатор nError – возвращаемое значение ошибки.

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

int _tmain(int argc, _TCHAR* argv[])

{

int nRank = 0;

int nResult = 0;

int* pSendBuf = NULL;

int nRecvBuf = 0;

int nSize = 0;

int i = 0;

MPI_Init(&argc, &argv);

MPI_Comm_rank(MPI_COMM_WORLD, &nRank);

MPI_Comm_size(MPI_COMM_WORLD, &nSize);

pSendBuf = (int *)malloc(nSize*sizeof(int));

for(i = 0; i

{

pSendBuf[i] = i;

}

nResult = MPI_Scatter(pSendBuf, 1, MPI_INT, &nRecvBuf, 1, MPI_INT, 0, MPI_COMM_WORLD);

if(nResult)

{

printf("MPI_Scatter returned error - %d", nResult);

}

else

{

printf("Process rank - %d", nRank);

printf(" nRecvBuf = %d\n", nRecvBuf);

}

MPI_Finalize();

return 0;

}

Результат работы функции для 4- процессов:

Process rank - 2 nRecvBuf = 2

Process rank - 3 nRecvBuf = 3

Process rank - 0 nRecvBuf = 0

Process rank - 1 nRecvBuf = 1

  1. Функции MPI_Reduce MPI_AllReduce

Функция MPI_Reduce

int nError MPI_Reduce(void* operand, void* result, int count, MPI_Datatype datatype, MPI_Op op,  int root, MPI_Comm comm)

operand- операнды функции result- результат, который сохраняется в корневом процессе count-количество ячеек в памяти, на которые ссылаются и операнд и результат datatype – тип данных op – оператор, с помощью которого производятся вычисления root- номер корневого процесса, в котором сохраняются результаты comm. – коммуникатор.

MPI_Reduce() объединяет операнды, сохраненные в *operand, используя оператор op и сохраняет результат в переменной *result корневого процесса root. И операнд, и результат ссылаются на count ячеек памяти с типом datatype. MPI_Reduce() должны вызывать все процессы в коммуникаторе comm. При вызове значения count, datatype и op должны быть одинаковыми в каждом процессе.

Пример использования. Функция суммирует ранги всех процессов и сохраняет их в нулевом:

int _tmain(int argc, _TCHAR* argv[])

{

int nSize = 0;

int nRank = 0;

int nResult = 0;

int nError = 0;

MPI_Init(&argc, &argv);

MPI_Comm_size(MPI_COMM_WORLD, &nSize);

MPI_Comm_rank(MPI_COMM_WORLD, &nRank);

nError = MPI_Reduce(&nRank, &nResult, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);

if(nError)

{

printf("MPI_Reduce returned Error %d\n", nError);

}

else

{

if(!nRank)

{

printf("Result is %d\n", nResult);

}

}

MPI_Finalize();

return 0;

}

Результат работы функции. Вывод полученного значения суммы номеров всех процессов(для 4-х процессов):

Result is 6

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

int MPI_Allreduce(void* sendbuf, void* recvbuf, int count,

MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)









Случайные файлы

Файл
33134.rtf
60876.rtf
Trans.doc
1317.rtf
42760.rtf




Чтобы не видеть здесь видео-рекламу достаточно стать зарегистрированным пользователем.
Чтобы не видеть никакую рекламу на сайте, нужно стать VIP-пользователем.
Это можно сделать совершенно бесплатно. Читайте подробности тут.