Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MPIBook.doc
Скачиваний:
104
Добавлен:
18.03.2015
Размер:
952 Кб
Скачать
☆

2.3. Функции коллективного взаимодействия

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

  1. функции коллективного обмена данными;

  2. функции синхронизации – барьеры;

  3. функции распределенных операций.

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

2.3.1. Функции коллективного обмена данными

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

Таблица 2.1

Категория

Функции

один-всем (broadcast)

MPI_Bcast

каждый-одному (gather)

MPI_Gather, MPI_Gatherv

один-каждому (scatter)

MPI_Scatter, MPI_Scatterv

все-каждому

(gather-to-all)

MPI_Allgather, MPI_Allgatherv

каждый-каждому

(all-to-all)

MPI_Alltoall, MPI_Alltoallv

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

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

  2. Метки сообщений указываются не пользователем, а назначаются автоматически системой MPI.

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

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

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

процессы

Данные

A

B

C

Процессы

данные

A

B

C

Функция

Рис. 2.1. Схема действия коллективной функции

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

▲ int MPI_Bcast(void *buf, int count,

MPI_Datatype datatype, int root,

MPI_Comm comm);

процессы

данные

A

процессы

данные

A

A

A

MPI_Bcast

Рис. 2.2. Схема действия функции MPI_Bcast

Входные параметры функции:

  • buf – адрес в памяти, начиная с которого

размещаются передаваемые данные;

  • count – количество рассылаемых элементов;

  • datatype – тип отправляемых данных;

  • root – номер процесса-отправителя сообщения;

  • comm – коммуникатор.

Таким образом, функция MPI_Bcast рассылает сообщение, хранящееся в буфере buf размером count элементов типа datatype из процесса, имеющего в коммуникационной группе с коммуникатором comm номер root, во все остальные процессы группы.

Функция MPI_Bcast осуществляет рассылку сообщения всем процессам группы, включая и процесс-отправитель.

П р и м е р.

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

int a[5]

…

MPI_Bcast(a, 5, MPI_INT, 0, MPI_COMM_WORLD)

Такого же результата можно добиться и с использованием функций точечного обмена данными, однако в этом случае значительно увеличится объем кода и возрастет количество коммуникационных операций:

int a[5]

int i;

…

if (MyID == 0)

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

MPI_Send(a,5,MPI_INT,i,777+i,

MPI_COMM_WORLD);

MPI_Recv(a,5,MPI_INT,0,777+MyID,

MPI_COMM_WORLD,&status);

Здесь, как и в приведенных ранее примерах, NumProc – количество процессов в основной группе с коммуникатором MPI_COMM_WORLD, MyID – номер процесса. ■

Для сбора данных со всех процессов на одном используется так называемый "совок" – функция MPI_Gather (см. рис. 2.3):

▲ int MPI_Gather(void *sbuf, int scount,

MPI_Datatype stype, void *rbuf,

int rcount, MPI_Datatype rtype,

int root, MPI_Comm comm);

процессы

данные

A

B

C

процессы

данные

A

B

C

MPI_Gather


Рис. 2.3. Схема действия функции MPI_Gather

Входные параметры функции:

  • sbuf – адрес в памяти на каждом процессе, начиная с

которого размещаются отправляемые данные;

  • scount – количество элементов, отправляемых с каждо-

го процесса;

  • stype – тип отправляемых данных;

  • rcount – количество принимаемых элементов от каж-

дого процесса;

  • rtype – тип принимаемых данных;

  • root – номер процесса, на котором осуществляется

сборка сообщений;

  • comm – коммуникатор.

Выходной параметр функции:

  • rbuf – адрес в памяти на процессе с номером root,

начиная с которого размещаются принимаемые

сообщения.

Таким образом, функция MPI_Gather собирает в буфер rbuf процесса root буферы sbuf со всех процессов.

Х а р а к т е р н ы е о с о б е н н о с т и ф у н к ц и и.

  1. Процесс-приемник сообщения root также отправляет данные самому себе.

  2. Значение rcount в процессе root представляет собой длину сообщения, получаемого от каждого процесса, а не суммарную длину сообщений от всех процессов.

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

П р и м е р.

Пусть во всех процессах группы, состоящей из 5 процессов (NumProc = 5), имеется вещественный массив AS из 10 элементов. Требуется собрать все массивы в единый общий массив AR в порядке следования номеров процессов на процессе с номером 2. Тогда достаточно одного вызова функции MPI_Gather:

double AS[10], AR[50];

…

MPI_Gather(AS, 10, MPI_DOUBLE, AR, 10,

MPI_DOUBLE, 2, MPI_COMM_WORLD);

…

■

Функция MPI_Gather обладает двумя серьезными недостатками:

  1. размер принимаемых сообщений от всех процессов должен быть одинаков;

  2. отсутствует механизм позиционного размещения принимаемых сообщений.

Поэтому в MPI введен расширенный (векторный) вариант этой функции, свободный от названных недостатков:

▲ int MPI_Gatherv(void *sbuf, int scount,

MPI_Datatype stype,

void *rbuf, int *rcounts,

int *displs,

MPI_Datatype rtype,

int root, MPI_Comm comm);

Входные параметры функции:

  • sbuf – адрес в памяти на каждом процессе, начиная

с которого размещаются отправляемые данные;

  • scount – количество элементов, отправляемых

с процесса;

  • stype – тип отправляемых данных;

  • rcounts – массив длин принимаемых от процессов

сообщений;

  • displs – массив позиций в приемном буфере,

по которым размещаются принимаемые

сообщения;

  • rtype – тип принимаемых данных;

  • root – номер процесса, на котором осуществляется

сборка сообщений;

  • comm – коммуникатор.

Выходной параметр функции:

  • rbuf – адрес в памяти на процессе с номером root,

начиная с которого размещаются принимаемые

сообщения.

Функция MPI_Gatherv позволяет отправлять разное количество элементов из различных процессов. Поэтому, в отличие от функции MPI_Gather, в процессе root параметры displs и rcounts являются уже не скалярами, а массивами.

Х а р а к т е р н ы е о с о б е н н о с т и ф у н к ц и и.

  1. Массивы displs и rcounts являются целочисленными с размерностью, равной числу процессов в группе.

  2. Длины сообщений в массиве rcounts и позиции размещения в массиве displs измеряются в количестве элементов типа rtype.

П р и м е р.

Пусть имеется группа из пяти процессов, в каждом из которых определен двумерный целочисленный массив AS[5][10]. Требуется осуществить выборочную сборку данных со всех процессов на третьем процессе по следующему правилу: с i-го процесса (i = 0,1…,4) выбираются (10–2i) первых значений из i-й строки массива AS и пересылаются в одномерный целочисленный массив AR. При этом расстояние между началами принятых блоков (в стандарте MPI для него используется обозначение stride) должно составлять 12 элементов (см. рис. 2.4).

Процессы

01234

Рис. 2.4. Схема сбора данных на процессе root

#include “mpi.h”

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

{

int NumProc; /* количество процессов */

int MyID; /* номер процесса */

int AS[5][10]; /* массив исходных данных */

int AR[60]; /* массив для размещения

принимаемых сообщений */

int rcounts[5]; /* массив длин сообщений */

int displs[5]; /* массив позиций,

по которым размещаются

принимаемые сообщения */

int stride=12; /* расстояние между

начальными позициями

размещения принятых

блоков */

int scount; /* количество элементов,

отправляемых с процесса */

int root=3; /* номер процесса,

на котором осуществляется

сборка сообщений */

int i; /* переменная цикла */

/* блок инициализации MPI */

MPI_Init(&argc, &argv);

MPI_Comm_size(MPI_COMM_WORLD, &NumProc);

MPI_Comm_rank(MPI_COMM_WORLD, &MyID);

. . .

/* определение количества элементов,

отсылаемых с каждого процесса */

scount=10-2*MyID;

/* заполнение массивов displs и rcounts

на процессе-сборщике сообщений */

if (MyID==root)

{

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

{

displs[i]=i*stride;

rcounts[i]=10–2*i;

}

}

/* сборка сообщений */

MPI_Gatherv(&AS[myid][0], scount, MPI_INT,

AR, rcounts, displs, MPI_INT,

root, MPI_COMM_WORLD);

. . .

/* завершение работы с MPI */

MPI_Finalize();

}

■

Обратной к MPI_Gather является функция MPI_Scatter – так называемый "разбрызгиватель" (см. рис. 2.5):

▲ int MPI_Scatter( void *sbuf, int scount,

MPI_Datatype stype, void *rbuf,

int rcount, MPI_Datatype rtype,

int root, MPI_Comm comm);

процессы

данные

A0

A1

A2

процессы

данные

A0

A1

A2

MPI_Scatter

Рис. 2.5. Схема действия функции MPI_Scatter

Входные параметры функции:

  • sbuf – адрес в памяти на процессе-отправителе сооб-

щения, начиная с которого размещаются от-

отправляемые данные;

  • scount – количество элементов, отправляемых каждому

процессу;

  • stype – тип отправляемых данных;

  • rcount – количество элементов, принимаемых каждым

процессом (длина принимаемого сообщения);

  • rtype – тип принимаемых данных;

  • root – номер процесса-отправителя сообщения;

  • comm – коммуникатор.

Выходной параметр функции:

  • rbuf – адрес в памяти на каждом процессе, начиная

с которого размещаются принимаемые

сообщения.

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

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

П р и м е р.

Пусть необходимо вещественный массив AS, состоящий из 10 элементов и хранящийся на первом процессе, распределить между всеми процессами группы, состоящей из 5 процессов. В результате в каждый процесс пересылается 2 элемента, которые записываются в массив AR.

double AS[10], AR[2]

. . .

MPI_Scatter(AS, 2, MPI_DOUBLE, AR, 2,

MPI_DOUBLE, 1, MPI_COMM_WORLD)

. . .

■

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

▲ int MPI_Scatterv(void *sbuf, int *scounts,

int *displs,

MPI_Datatype stype,

void *rbuf, int rcount,

MPI_Datatype rtype,int root,

MPI_Comm comm);

Входные параметры функции:

  • sbuf – адрес в памяти на процессе-отправителе

сообщения, начиная с которого размещаются

отправляемые данные;

  • scounts – массив, содержащий количество элементов

в каждой части, на которые разбивается

сообщение;

  • displs – массив позиций, определяющий начальные

положения каждой части сообщения;

  • stype – тип отправляемых данных;

  • rcount – количество элементов, принимаемых текущим

процессом (длина принимаемого сообщения);

  • rtype – тип принимаемых данных;

  • root – номер процесса-отправителя сообщения;

  • comm – коммуникатор.

Выходной параметр функции:

  • rbuf – адрес в памяти на каждом процессе, начиная

с которого размещаются принимаемые

сообщения.

Функция MPI_Scatterv является обратной для MPI_Gatherv.

П р и м е р.

Рассмотрим операцию, обратную рассмотренной в предыдущем примере. Пусть требуется распределить массив ASN[60] со структурой, аналогичной структуре массива AR[60] предыдущего примера (см. рис. 2.4 на с. 38), по массивам ARN[5][10] по правилу: на процессе с номером i сообщения размещаются с нулевой позиции i-й строки.

#include “mpi.h”

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

{

int NumProc; /* количество процессов */

int MyID; /* номер процесса */

int ASN[60]; /* массив исходных данных */

int ARN[5][10]; /* массив для размещения

принимаемого сообщения */

int scounts[5]; /* массив длин отдельных

сообщений (длин частей

исходного сообщения) */

int displs[5]; /* массив начальных позиций

частей, на которые

разбивается исходное

сообщение */

int stride=12; /* расстояние между

начальными позициями

размещения частей

исходного сообщения */

int rcount; /* количество элементов,

принимаемых процессом */

int root=3; /* номер процесса,

с которого осуществляется

рассылка */

int i; /* переменная цикла */

/* блок инициализации MPI */

MPI_Init(&argc, &argv);

MPI_Comm_size(MPI_COMM_WORLD, &NumProc);

MPI_Comm_rank(MPI_COMM_WORLD, &MyID);

. . .

/* определение длин сообщений,

принимаемых каждым процессом */

rcount=10-2*MyID;

/* заполнение массивов displs и scounts

на процессе-отправителе сообщений */

if (MyID==root)

{

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

{

displs[i]=i*stride;

scounts[i]=10–2*i;

}

}

/* передача сообщений */

MPI_Scatterv(ASN, scounts, displs, MPI_INT,

&ARN[MyID][0], rcount, MPI_INT,

root, MPI_COMM_WORLD);

. . .

/* завершение работы с MPI */

MPI_Finalize();

}

■

процессы

данные

A

B

C

процессы

данные

A

B

C

A

B

C

A

B

C

MPI_Allgather

Рис. 2.6. Схема действия функции MPI_Gather

Дальнейшим обобщением функции MPI_Gather является функция MPI_Allgather, которая размещает собранные данные не на одном выделенном процессе root, а на всех процессах группы, участвующих в коллективной операции обмена данными (см. рис. 2.6):

▲ int MPI_Allgather(void *sbuf, int scount,

MPI_Datatype stype,

void *rbuf, int rcount,

MPI_Datatype rtype,

MPI_Comm comm);

Входные параметры функции:

  • sbuf – адрес в памяти на каждом процессе, начиная

с которого размещаются отправляемые данные;

  • scount – количество элементов, отправляемых

с каждого процесса;

  • stype – тип отправляемых данных;

  • rcount – количество принимаемых элементов

от каждого процесса;

  • rtype – тип принимаемых данных;

  • comm – коммуникатор.

Выходной параметр функции:

  • rbuf – адрес в памяти, начиная с которого

размещаются принимаемые сообщения.

Как и предыдущие функции, MPI_Allgather имеет векторный аналог:

▲ int MPI_Allgatherv(void *sbuf, int scount,

MPI_Datatype stype,

void *rbuf, int *rcounts,

int *displs,

MPI_Datatype rtype,

MPI_Comm comm);

Входные параметры функции:

  • sbuf – адрес в памяти на каждом процессе, начиная

с которого размещаются отправляемые данные;

  • scount – количество элементов, отправляемых

с процесса;

  • stype – тип отправляемых данных;

  • rcounts – массив длин принимаемых от процессов

сообщений;

  • displs – массив позиций в приемном буфере,

по которым размещаются принимаемые

сообщения;

  • rtype – тип принимаемых данных;

  • comm – коммуникатор.

Выходной параметр функции:

  • rbuf – адрес в памяти, начиная с которого

размещаются принимаемые сообщения.

Результат применения MPI_Allgather (MPI_Allgatherv) аналогичен результату n последовательных вызовов функции MPI_Gather (MPI_Gatherv) со значениями root = 0, 1, …, n (n – число процессов в группе).

П р и м е р.

Пусть во всех процессах группы, состоящей из 5 процессов (NumProc = 5), имеется вещественный массив AS из 10 элементов. Требуется собрать все массивы в единый общий массив AR в порядке следования номеров процессов, и этот массив должен находиться на всех процессах группы.

double AS[10], AR[50];

…

MPI_Allgather(AS, 10, MPI_DOUBLE, AR, 10,

MPI_DOUBLE, MPI_COMM_WORLD);

■

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

▲ int MPI_Alltoall(void *sbuf, int scount,

MPI_Datatype stype,

void *rbuf, int rcount,

MPI_Datatype rtype,

MPI_Comm comm);

Входные параметры функции:

  • sbuf – адрес в памяти на каждом процессе, начиная

с которого размещаются отправляемые данные;

  • scount – количество элементов, отправляемых

с каждого процесса;

  • stype – тип отправляемых данных;

  • rcount – количество принимаемых элементов

от каждого процесса;

  • rtype – тип принимаемых данных;

  • comm – коммуникатор.

Выходной параметр функции:

  • rbuf – адрес в памяти, начиная с которого

размещаются принимаемые сообщения.

процессы

данные

A0

A1

A2

B0

B1

B2

C0

C1

C2

процессы

данные

A0

B0

C0

A1

B1

C1

A2

B2

C2

MPI_Alltoall

Рис. 2.7. Схема действия функции MPI_Alltoall

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

▲ int MPI_Alltoallv (void *sbuf, int *scounts,

int *sdispls,

MPI_Datatype stype,

void *rbuf, int *rcounts,

int *rdispls,

MPI_Datatype rtype,

MPI_Comm comm);

Входные параметры функции:

  • sbuf – адрес в памяти на каждом процессе, начиная

с которого размещаются отправляемые данные;

  • scounts – массив, содержащий количество элементов

в каждой части, на которые разбивается

сообщение на каждом процессе;

  • sdispls – массив позиций, определяющий начальные

положения каждой части сообщения на каждом

процессе;

  • stype – тип отправляемых данных;

  • rcounts – массив длин принимаемых от процессов

сообщений;

  • displs – массив позиций в приемном буфере,

по которым размещаются принимаемые

сообщения;

  • rtype – тип принимаемых данных;

  • comm – коммуникатор.

Выходной параметр функции:

  • rbuf – адрес в памяти, начиная с которого

размещаются принимаемые сообщения.

2.3.2. Синхронизация процессов

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

▲ int MPI_Barrier(MPI_Comm comm);

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

2.3.3. Функции поддержки распределенных операций

В MPI имеется четыре основные функции, выполняющие распределенные операции, отличающиеся друг от друга размещением результатов:

▲ int MPI_Reduce(void *sbuf, void *rbuf,

int count, MPI_Datatype datatype,

MPI_Op op, int root,

MPI_Comm comm);

▲ int MPI_Allreduce(void *sbuf, void *rbuf,

int count,

MPI_Datatype datatype,

MPI_Op op, MPI_Comm comm);

▲ int MPI_Reduce_scatter(void *sbuf, void *rbuf,

int *rcounts,

MPI_Datatype datatype,

MPI_Op op,

MPI_Comm comm);

▲ int MPI_Scan(void *sbuf, void *rbuf, int count,

MPI_Datatype datatype, MPI_Op op,

MPI_Comm comm);

Входные параметры функций:

  • sbuf – адрес в памяти на каждом процессе,

по которому хранятся исходные данные

для распределенной операции;

  • count – количество элементов в sbuf;

  • rcounts – массив, определяющий количество элементов,

распределяемых каждому процессу;

  • datatype – тип данных, над которыми производится

распределенная операция;

  • root – номер процесса, на котором осуществляется

размещение результата выполнения операции;

  • op – название распределенной операции;

  • comm – коммуникатор.

Выходной параметр функций:

  • rbuf – адрес в памяти, по которому размещаются

результаты выполнения операции.

Назначение всех приведенных функций одинаково: над данными, хранящимися в sbuf в каждом процессе группы с коммуникатором comm, проделывается операция op, результат которой записывается в rbuf. Например, если в каждом процессе в sbuf хранится вектор размером m, а операция op представляет собой операцию суммирования MPI_SUM, то rbuf также будет вектором из m элементов, представляющий собой сумму векторов, содержащихся во всех процессах. Данные из sbuf и rbuf должны иметь одинаковый размер count и одинаковый тип datatype. Функции различаются способом хранения результатов:

MPI_Reduce – помещает результат в процесс с номером

root;

MPI_Allreduce – рассылает результат всем процессам;

MPI_Reduce_scatter – рассылает каждому процессу не весь

массив-результат, а его часть, длина которой определяется массивом rcounts; при этом размер исходных массивов во всех задачах одинаков и равен сумме длин частей результирующего массива;

MPI_Scan – содержимое массива-результата в i-м

процессе является результатом выполнения операции op над массивами, содержащимися в процесах с номерами от 0 до i включительно; при этом результат рассылается во все процессы.

В функции MPI_Reduce_scatter массив rcounts должен быть идентичен во всех процессах.

В MPI существует 12 предопределенных операций:

  • MPI_MAX – поиск поэлементного максимума;

  • MPI_MIN – поиск поэлементного минимума;

  • MPI_SUM – вычисление суммы векторов;

  • MPI_PROD – вычисление поэлементного произведения

векторов;

  • MPI_LAND – логическое “И”;

  • MPI_LOR – логическое “ИЛИ”;

  • MPI_LXOR – логическое исключающее “ИЛИ”;

  • MPI_BAND – бинарное “И”;

  • MPI_BOR – бинарное “ИЛИ”;

  • MPI_BXOR – бинарное исключающее ИЛИ;

  • MPI_MAXLOC– поиск индексированного максимума;

  • MPI_MINLOC– поиск индексированного минимума.

Все перечисленные операции являются ассоциативными и коммутативными. Определение операций MPI_MAXLOC и MPI_MINLOC приведено в стандарте MPI [1].

П р и м е р.

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

#include <stdio.h>

#include “mpi.h”

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

{

int S=0; /* вспомогательная переменная */

int SAdd; /* искомая сумма */

int NumProc; /* количество процессов */

int MyID; /* номер процесса */

/* блок инициализации MPI */

MPI_Init(&argc, &argv);

MPI_Comm_size(MPI_COMM_WORLD, &NumProc);

MPI_Comm_rank(MPI_COMM_WORLD, &MyID);

/* вычисление куба числа

на каждом процессоре */

S=MyID+1;

S=S*S*S;

/* суммирование результатов на всех

процессах c использованием функции

распределенных операций */

MPI_Reduce(&S, &SAdd, 1,MPI_INT,MPI_SUM, 0,

MPI_COMM_WORLD);

/* печать результата на экран */

if (MyID == 0)

printf(“Sum is equal to %d”,SAdd);

/* завершение работы с MPI */

MPI_Finalize();

/* нормальное завершение работы программы */

return 0;

}

■

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

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

    1. Определение времени выполнения

параллельной программы

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

▲ double MPI_Wtime();

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

Х а р а к т е р н ы е о с о б е н н о с т и ф у н к ц и и.

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

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

Для определения точности измерения времени функцией MPI_Wtime предусмотрена другая MPI-функция, которая возвращает значение частоты внутреннего таймера:

▲ double MPI_Wtick();

Так, если значением этой функции является число 0,01, то это означает, что счетчик времени изменяется 100 раз в секунду.

П р и м е р.

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

double tstart, tfinish;

. . .

/* Cинхронизация процессов */

MPI_Barrier(MPI_Comm_world);

/* Определение времени начала выполнения

участка кода */

tstart = MPI_Wtime();

/* Участок кода, время выполнения которого

измеряется */

. . .

/* Синхронизация процессов */

MPI_Barrier(MPI_Comm_world);

/* Определение искомого времени выполнения

участка кода */

tfinish = MPI_Wtime()-tstart;

. . .

■

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]