Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

МПП_Примеры2

.doc
Скачиваний:
14
Добавлен:
12.02.2015
Размер:
87.55 Кб
Скачать

Передача и прием сообщений процессами – это базовый коммуни-

кационный механизм MPI. Их использование иллюстрируем примерами:

#include "mpi.h"

main( int argc, char **argv)

{ char message[20];

int myrank;

MPI_Status status;

MPI_Init( &argc, &argv );

MPI_Comm_rank( MPI_COMM_WORLD, &myrank );

if (myrank == 0) /* код для процесса 0 */

{ strcpy(message,"Hello, there");

MPI_Send(message,strlen(message),MPI_CHAR,1,99,

MPI_COMM_WORLD);

}

else /* код для процесса 1 */

{ MPI_Recv(message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD,

&status);

printf("received :%s:\n", message);

}

MPI_Finalize();

}

В этом примере процесс с номером 0 (myrank = 0) посылает со-

общение процессу с номером 1, используя операцию посылки

MPI_Send. Эта операция описывает буфер посылающего процесса, из

которого извлекаются посылаемые данные. В приведенном примере

посылающий буфер состоит из накопителя в памяти процесса 0, со-

держащего переменную message. Размещение, размер и тип буфера

посылающего процесса описываются первыми тремя параметрами

операции send. Посланное сообщение будет содержать 13 символов

этой переменной. Операция посылки также связывает с сообщением

его атрибуты. Атрибуты определяют номер процесса-получателя со-

общения и содержат различную информацию, которая может быть

использована операцией receive, чтобы выбрать определенное сооб-

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

описывают атрибуты посланного сообщения. Процесс 1 (myrank = 1)

получает это сообщение, используя операцию приема MPI_Recv, и

данные сообщения записываются в буфер процесса-получателя. В

приведенном примере буфер получателя состоит из накопителя в па-

мяти процесса один, содержащего строку message. Первые три пара-

метра операции приема описывают размещение, размер и тип буфера

приема. Следующие три параметра необходимы для выбора входного

сообщения. Последний параметр необходим для возврата информации

о только что полученном сообщении.

Пример 4.2. Сбор 100 целых чисел с каждого процесса группы в кор-

невой процесс (рис. 4.2).

MPI_Comm comm;

int gsize,sendarray[100];

int root, *rbuf;

MPI_Comm_size( comm, &gsize);

rbuf = (int *)malloc(gsize*100*sizeof(int));

MPI_Gather(sendarray,100, MPI_INT, rbuf,100,MPI_INT,root, comm);

Пример 4.3 Предыдущий пример модифицирован – только корневой

процесс назначает память для буфера приема.

MPI_Comm comm;

int gsize,sendarray[100];

int root, myrank, *rbuf;

MPI_Comm_rank( comm, myrank);

if ( myrank == root)

{

MPI_Comm_size( comm, &gsize);

rbuf = (int *)malloc(gsize*100*sizeof(int));

}

MPI_Gather(sendarray,100,MPI_INT, rbuf,100, MPI_INT, root,comm);

Пример 4.5. Каждый процесс посылает 100 чисел int корневому про-

цессу, но каждое множество (100 элементов) размещается с некото-

рым шагом (stride) относительно конца размещения предыдущего

множества. Для этого нужно использовать MPI_GATHERV и аргу-

мент displs. Полагаем, что stride 100

MPI_Comm comm;

int gsize,sendarray[100], root, *rbuf, stride, *displs,i,*rcounts;

MPI_Comm_size( comm, &gsize);

rbuf = (int *)malloc(gsize*stride*sizeof(int));

displs = (int *)malloc(gsize*sizeof(int));

rcounts = (int *)malloc(gsize*sizeof(int));

for (i=0; i<gsize; ++i) {

displs[i] = i*stride;

rcounts[i] = 100;

}

MPI_Gatherv( sendarray, 100, MPI_INT, rbuf, rcounts, displs,

MPI_INT, root, comm);

Программа неверна, если stride < 100.

Пример 4.6. Со стороны процесса-получателя пример такой же, как и

4.5, но посылается 100 чисел типа int из 0-го столбца массива 100×150

чисел типа int

MPI_Comm comm;

int gsize,sendarray[100][150], root, *rbuf, stride, *displs,i,*rcounts;

MPI_Datatype stype;

MPI_Comm_size( comm, &gsize);

rbuf = (int *)malloc(gsize*stride*sizeof(int));

displs = (int *)malloc(gsize*sizeof(int));

rcounts = (int *)malloc(gsize*sizeof(int));

for (i=0; i<gsize; ++i) {

displs[i] = i*stride;

rcounts[i] = 100; }

/* Create datatype for 1 column of array */

MPI_Type_vector( 100, 1, 150, MPI_INT, &stype);

MPI_Type_commit( &stype );

MPI_Gatherv( sendarray, 1, stype, rbuf, rcounts, displs, MPI_INT, root, comm);

Пример 4.11. Обратен примеру 4.2, MPI_SCATTER рассылает 100

чисел из корневого процесса каждому процессу в группе (рис. 4.7).

MPI_Comm comm;

int gsize,*sendbuf;

int root, rbuf[100];

MPI_Comm_size( comm, &gsize);

sendbuf = (int *)malloc(gsize*100*sizeof(int));

MPI_Scatter( sendbuf, 100, MPI_INT, rbuf, 100, MPI_INT, root, comm);

Пример 4.14. Сбор 100 чисел типа int от каждого процесса в группе

для каждого процесса.

MPI_Comm comm;

int gsize,sendarray[100], *rbuf;

MPI_Comm_size( comm, &gsize);

rbuf = (int *)malloc(gsize*100*sizeof(int));

MPI_Allgather( sendarray, 100, MPI_INT, rbuf, 100, MPI_INT, comm);

После исполнения вызова каждый процесс содержит конкатена-

цию данных всей группы.

Редукция

Процедура вычисляет скалярное произведение двух

векторов, распределенных в группе процессов, и возвращает результат

в нулевой узел.

SUBROUTINE PAR_BLAS1(m, a, b, c, comm)

REAL a(m), b(m) ! локальная часть массива

REAL c ! результат (на узле ноль)

REAL sum

INTEGER m, comm, i, ierr

! локальная сумма

sum = 0.0

DO i = 1, m

sum = sum + a(i)*b(i)

END DO

! глобальная сумма

CALL MPI_REDUCE(sum, c, 1, MPI_REAL, MPI_SUM, 0, comm, ierr)

RETURN

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

которые распределены в группе процессов, и возвращает результат в

нулевой узел.

SUBROUTINE PAR_BLAS2(m, n, a, b, c, comm)

REAL a(m), b(m,n) ! локальная часть массива

REAL c(n) ! результат

REAL sum(n)

INTEGER n, comm, i, j, ierr

! локальная сумма

DO j= 1, n

sum(j) = 0.0

DO i = 1, m

sum(j) = sum(j) + a(i)*b(i,j)

END DO

END DO

! глобальная сумма

CALL MPI_REDUCE(sum, c, n, MPI_REAL, MPI_SUM, 0, comm, ierr)

RETURN

Пример 4.17. Каждый процесс имеет массив 30 чисел типа double.

Для каждой из 30 областей надо вычислить значение и номер процесса, содержащего наибольшее значение.

/* каждый процесс имеет массив из чисел двойной точности: ain[30]*/

double ain[30], aout[30];

int ind[30];

struct {

double val;

int rank;

} in[30], out[30];

int i, myrank, root;

MPI_Comm_rank(MPI_COMM_WORLD, &myrank);

for (i=0; i<30; ++i) {

in[i].val = ain[i];

in[i].rank = myrank;

}

MPI_Reduce(in,out,30,MPI_DOUBLE_INT,MPI_MAXLOC,root,comm );

/* в этой точке результат помещается на корневой процесс */

if (myrank == root) { /* читаются выходные номера */

for (i=0; i<30; ++i) {

aout[i] = out[i].val;

ind[i] = out[i].rank; /* номер обратно преобразуется в целое */

}

Пример 4.18. Каждый процесс имеет не пустой массив чисел. Требу-

ется найти минимальное глобальное число, номер процесса, храняще-

го его, его индекс в этом процессе.

#define LEN 1000

float val[LEN]; /* локальный массив значений */

int count; /* локальное количество значений */

int myrank, minrank, minindex;

float minval;

struct {

float value;

int index;

} in, out;

/* локальный minloc */

in.value = val[0];

in.index = 0;

for (i=1; i < count; i++)

if (in.value > val[i])

{ in.value = val[i];

in.index = i;

}

/* глобальный minloc */

MPI_Comm_rank(MPI_COMM_WORLD, &myrank);

in.index = myrank*LEN + in.index;

MPI_Reduce(in, out, 1, MPI_FLOAT_INT, MPI_MINLOC, root, comm );

/* в этой точке результат помещается на корневой процесс */

if (myrank == root)

{ minval = out.value;

minrank = out.index / LEN;

minindex = out.index % LEN;

}

Пример 4.19. Процедура вычисляет произведение вектора и массива,

которые распределены по всем процессам группы, и возвращает ответ

всем узлам.

SUBROUTINE PAR_BLAS2(m, n, a, b, c, comm)

! локальная часть массива

REAL a(m), b(m,n)

! результат

REAL c(n)

REAL sum(n)

INTEGER n, comm, i, j, ierr

! локальная сумма

DO j= 1, n

sum(j) = 0.0

DO i = 1, m

sum(j) = sum(j) + a(i)*b(i,j)

END DO

END DO

! глобальная сумма

CALL MPI_ALLREDUCE(sum,c,n,MPI_REAL,MPI_SUM,comm,ierr)

! возвращение результата всем узлам

RETURN