Системное программное обеспечение - Учебное пособие (Терехин А.Н.)

4.20коллективный обмен, совмещенный с обработкой данных.

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

#include <mpi.h>

int MPI_Reduce(void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm);

Функция  MPI_Reduce() является своего рода расширением функции MPI_Gather(): ветвь с номером root является получателем данных, отправителями являются все ветви. Параметры sendbuf, datatype, count имеют смысл для всех ветвей и задают адрес буфера с данными для отправки, тип отправляемых данных и их количество. Тип и количество данных должны совпадать во всех ветвях. Однако в процессе получения данных над ними поэлементно производится операция, задаваемая параметром op, результат которой заносится в соответствующий элемент буфера recvbuf ветви с номером root. Так, например, если параметр op равен константе MPI_SUM, что означает суммирование элементов, то в i-й элемент буфера recvbuf ветви с номером root будет записана сумма i-х элементов буферов sendbuf всех ветвей. Параметр recvbuf имеет смысл лишь для ветви с номером root, для всех остальных ветвей он игнорируется.

Для описания операций в MPI введен специальный тип MPI_Op. MPI предоставляет ряд констант этого типа, описывающих стандартные операции, такие как суммирование (MPI_SUM), умножение (MPI_PROD), вычисление максимума и минимума (MPI_MAX и MPI_MIN) и т.д. Кроме того, у программиста существует возможность описывать новые операции.

 

Функция MPI_Allreduce() представляет собой аналог MPI_Reduce() с той лишь разницей, что результирующий массив формируется на всех ветвях (и, соответственно, параметр recvbuf также должен быть задан на всех ветвях):

#include <mpi.h>

int MPI_Allreduce(void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);

 

Функция MPI_Reduce_scatter() сочетает в себе возможности MPI_Reduce() и MPI_Scatter():

#include <mpi.h>

int MPI_Reduce_scatter(void* sendbuf, void* recvbuf, int *recvcounts, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);

В процессе ее выполнения сначала над массивами sendbuf поэлементно производится операция, задаваемая параметром op, а затем результирующий массив разделяется  на N частей, где N – количество ветвей в коммуникационном контексте, и каждая из ветвей получает в буфере recvbuf свою часть в соответствии со своим номером. Элементы массива recvcounts задают количество элементов, которое должно быть отправлено каждой ветви, что позволяет каждой ветви получать порции данных разной длины.

 

Наконец, функция MPI_scan()  представляет собой некоторое сужение функции MPI_AllReduce():

#include <mpi.h>

int MPI_Scan(void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);

В буфере recvbuf ветви с номером i после возврата из этой функции будет находиться результат поэлементного применения операции op к буферам sendbuf ветвей с номерами от 0 до i включительно. Очевидно, что в буфере recvbuf ветви с номером N-1 будет получен тот же результат, что и для аналогичного вызова MPI_Allreduce().