Вы находитесь на странице: 1из 5

ТЕХНОЛОГИЯ ПАРАЛЛЕЛЬНОГО ПРОГРАММИРОВАНИЯ MPI

Лекция 5. Коллективные операции обмена данными

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


коммуникатора. Функция коллективного взаимодействия должна быть вызвана каждым
процессом, возможно со своими индивидуальными параметрами. Как и для блокирующих
функций приема-передачи, возврат из функций коллективного обмена означает гарантию
корректности (безопасности) последующего использования буферов обмена.
По стандарту MPI гарантируется, что обмены, производимые процессами в рамках
коллективных операций, не будут пересекаться с обменами типа “точка-точка”. Также в
коллективных операциях не используются пользовательские теги – корректность коллективных
операций обеспечивается строгой упорядоченностью вызовов соответствующих функций.
Некорректный порядок коллективных операций может привести к блокировке программы.

1) Барьерная синхронизация
int MPI_Barrier (MPI_Comm comm);
Функция используется для барьерной синхронизации процессов. Процессы, вызвавшие
эту функцию, блокируются до тех пор, пока эта функция не будет вызвана всеми
процессами в коммуникаторе. После этого все процессы коммуникатора разблокируются.
2) Широковещательная рассылка данных
int MPI_Bcast (void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm);
Функция рассылает count последовательно расположенных элементов типа datatype
находящихся в буфере buf процесса root всем процессам коммуникатора comm. Для всех
процессов, не являющихся источником сообщения, данные принимаются в буфер buf.
Значение начала буфера buf для каждого процесса индивидуально. Параметры
сообщения count и datatype также могут отличаться, с единственным требованием на
идентичность сигнатур типов.
3) Сборка данных
int MPI_Gather (void *sbuf, int scount, MPI_Datatype sdatatype,
void *rbuf, int rcount, MPI_Datatype rdatatype,
int root, MPI_Comm comm);
Каждый процесс в коммуникаторе comm, включая root, посылает содержимое своего
буфера sbuf, а именно scount последовательно расположенных элементов типа datatype
процессу root. Получаемые процессом root данные преобразуются в формат
(rcount,rdatatype) и укладываются последовательно в соответствии с нумерацией
процессов в буфер rbuf. Эта операция эквивалентна набору операций:
MPI_Send (sbuf, scount, sdatatype, root, …);
if (rank==root)
{
for (i = 0; i < n; i++)
MPI_Recv (rbuf+i*rcount*extent(rdatatype), rcount, rdatatype, i, …);
}

int MPI_Gatherv (void *sbuf, int scount, MPI_Datatype sdatatype,

1
void *rbuf, int *rcount, int *rdispl, MPI_Datatype rdatatype,
int root, MPI_Comm comm);
Модификация-обобщение функции MPI_Gather. Каждый процесс в коммуникаторе
comm, включая root, посылает содержимое своего буфера sbuf, а именно scount
последовательно расположенных элементов типа datatype процессу root. Получаемые
процессом root данные i-го процесса преобразуются в формат (rcount[i],rdatatype) и
укладываются со смещением rdispl[i] элементов типа rdatatype в буфер rbuf. Эта
операция эквивалентна набору операций:
MPI_Send (sbuf, scount, sdatatype, root, …);
if (rank==root)
{
for (i = 0; i < n; i++)
MPI_Recv (rbuf+rdispl[i]*extent(rdatatype), rcount[i], rdatatype, i, …);
}

4) Распределение данных
int MPI_Scatter (void *sbuf, int scount, MPI_Datatype sdatatype,
void *rbuf, int rcount, MPI_Datatype rdatatype,
int root, MPI_Comm comm);
Операция MPI_Scatter является обратной операцией к MPI_Gather. Процесс root
осуществляет фрагментирование содержимого буфера sbuf на size(comm)
последовательно расположенных фрагментов. Каждый фрагмент содержит scount
последовательно расположенных элементов типа datatype. Затем каждый фрагмент
передается соответствующему процессу: i-й фрагмент i-му процессу. Каждый процесс в
коммуникаторе comm, включая root, принимает данные, преобразует в формат
(rcount,rdatatype) и укладывает их в буфер rbuf. Эта операция эквивалентна набору
операций:
if (rank==root)
{
for (i = 0; i < n; i++)
MPI_Send (sbuf+i*scount*extent(sdatatype), scount, sdatatype, i, …);
}
MPI_Recv (rbuf, rcount, rdatatype, root, …);

int MPI_Scatterv (void *sbuf, int *scount, int *displ, MPI_Datatype sdatatype,
void *rbuf, int rcount, MPI_Datatype rdatatype,
int root, MPI_Comm comm);
Операция MPI_Scatterv является обратной операцией к MPI_Gatherv. Процесс root
осуществляет фрагментирование содержимого буфера sbuf на size(comm) фрагментов.
Каждый i-й фрагмент содержит scount[i] последовательно расположенных элементов
типа datatype со смещением displ[i] относительно начала буфера sbuf. Затем каждый
фрагмент передается соответствующему процессу: i-й фрагмент i-му процессу. Каждый
процесс в коммуникаторе comm, включая root, принимает данные, преобразует в формат
(rcount,rdatatype) и укладывает их в буфер rbuf. Эта операция эквивалентна набору
операций:
2
if (rank==root)
{
for (i = 0; i < n; i++)
MPI_Send (sbuf+displ[i]*extent(sdatatype), scount[i], sdatatype, i, …);
}
MPI_Recv (rbuf, rcount, rdatatype, root, …);

5) Сборка-распределение данных (“каждый с каждым”)


int MPI_Allgather (void *sbuf, int scount, MPI_Datatype sdatatype,
void *rbuf, int rcount, MPI_Datatype rdatatype,
MPI_Comm comm);
Каждый процесс в коммуникаторе comm, посылает содержимое своего буфера sbuf, а
именно scount последовательно расположенных элементов типа datatype каждому
процессу. В каждом процессе получаемые данные преобразуются в формат
(rcount,rdatatype) и укладываются последовательно в соответствии с нумерацией
процессов в буфер rbuf. Результат операции MPI_Allgather фактически идентичен
результату операции MPI_Gather, при условии, что результат получается на всех
процессах.
for (i = 0; i < n; i++)
MPI_Gather (sbuf, scount, sdatatype, rbuf, rcount, rdatatype, i, comm.);

int MPI_Allgatherv (void *sbuf, int scount, MPI_Datatype sdatatype,


void *rbuf, int *rcount, int *rdispl, MPI_Datatype rdatatype,
MPI_Comm comm);
Модификация-обобщение функции MPI_Allgather. Каждый процесс в коммуникаторе
comm посылает содержимое своего буфера sbuf, а именно scount последовательно
расположенных элементов типа datatype каждому процессу. В каждом процессе
получаемые данные i-го процесса преобразуются в формат (rcount[i],rdatatype) и
укладываются со смещением rdispl[i] элементов типа rdatatype в буфер rbuf. Эта
операция эквивалентна набору операций:
for (i = 0; i < n; i++)
MPI_Gatherv (sbuf, scount, sdatatype, rbuf, rcount, rdatatype, i, comm.);

int MPI_Alltoall (void *sbuf, int scount, MPI_Datatype sdatatype,


void *rbuf, int rcount, MPI_Datatype rdatatype,
MPI_Comm comm);
Каждый процесс осуществляет фрагментирование содержимого буфера sbuf на
size(comm) последовательно расположенных фрагментов. Каждый фрагмент содержит
scount последовательно расположенных элементов типа datatype. Процесс i передает
каждый фрагмент соответствующему процессу: j-й фрагмент j-му процессу. Процесс j
принимает данные от процесса i, преобразует в формат (rcount,rdatatype) и укладывает их
в буфер rbuf в сегмент i. Эта операция эквивалентна набору операций:
for (i = 0; i < n; i++)
MPI_Send (sbuf+i*scount*extent(sdatatype), scount, sdatatype, i, …);
for (i = 0; i < n; i++)
3
MPI_Recv (rbuf+i*rcount*extent(sdatatype), rcount, rdatatype, i, …);

int MPI_Alltoallv (void *sbuf, int *scount, int *sdispl, MPI_Datatype sdatatype,
void *rbuf, int *rcount, int *rdispl, MPI_Datatype rdatatype,
MPI_Comm comm);
Модификация-обобщение функции MPI_Alltoall. Каждый процесс осуществляет
фрагментирование содержимого буфера sbuf на size(comm) фрагментов. Фрагмент j
содержит scount[j] последовательно расположенных элементов типа sdatatype со
смещением sdispl[j] элементов типа sdatatype от начала буфера sbuf. Процесс i передает
каждый фрагмент соответствующему процессу: j-й фрагмент j-му процессу. Процесс j
принимает данные от процесса i, преобразует в формат (rcount[i],rdatatype) и укладывает
их в буфер rbuf со смещением rdispl[j] элементов типа rdatatype. Эта операция
эквивалентна набору операций:
for (i = 0; i < n; i++)
MPI_Send (sbuf+sdispl[i]*extent(sdatatype), scount[i], sdatatype, i, …);
for (i = 0; i < n; i++)
MPI_Recv (rbuf+rdispl[i]*extent(sdatatype), rcount[i], rdatatype, i, …);

6) Сборка данных с выполнением заданной операции над данными


int MPI_Reduce (void *sbuf, void *rbuf, int count, MPI_Datatype datatype,
MPI_Op op, int root, MPI_Comm comm);
Функция выполняет count независимых операций op над данными из буфера sbuf.
Результат операции над i-ми элементами буферов sbuf укладывается в i-ю позицию
буфера rbuf процесса root. В MPI предусмотрен набор предопределенных операций:
 MPI_SUM (суммирование),
 MPI_MAX (поиск максимума),
 MPI_MIN (поиск минимума),
 MPI_PROD (произведение),
 MPI_LAND (логическое AND),
 MPI_BAND (побитовое AND) и т.п.

Кроме предопределенных операций, пользователь может задать собственную операцию.


int MPI_Op_create (MPI_User_function *function, int commute, MPI_Op *op);
typedef void MPI_User_function (void *invec, void *inoutvec, int *len,
MPI_Datatype *datatype);
Функция MPI_Op_create создает операцию op привязанную к пользовательской функции
function. Предполагается, что операция op ассоциативна. Флаг commute фиксирует
коммутативность операции: commute=1, если операция коммутативна, в противном
случае, 0. Пользовательская функция имеет четыре аргумента с семантикой: invec –
первый операнд-массив длины len элементов типа datatype; inoutvec – второй операнд-
массив длины len и он же результирующий массив, т.е. inoutvec[i]=Operation (invec[i],
inoutvec[i]).

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


int MPI_Op_free (MPI_Op *op);
4
Другие варианты сборки данных с выполнением операции над ними:
int MPI_Allreduce (void *sbuf, void *rbuf, int count, MPI_Datatype datatype,
MPI_Op op, MPI_Comm comm);
Функция выполняет count независимых операций op над данными из буфера sbuf.
Результат операции над i-ми элементами буферов sbuf укладывается в i-ю позицию
буфера rbuf в каждом процессе.

int MPI_Scan (void *sbuf, void *rbuf, int count, MPI_Datatype datatype,
MPI_Op op, MPI_Comm comm);
Функция выполняет count независимых операций op над данными из буфера sbuf.
Процесс i выполняет count операций над элементами из буфера sbuf процессов от 0 до i-
го включительно. Результат операции укладывается в буфер rbuf процесса. Таким
образом, каждый процесс кроме последнего содержит в буфере rbuf частичный результат
операции op. Полный результат содержится в последнем процессе коммуникатора.