Коллективные обмены
Операции приведения
Операция приведения (редукции)
Операция приведения, результат которой передается одному процессу
int MPI_Reduce(void *buf, void *result, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm) MPI_Reduce(buf, result, count, datatype, op, root, comm, ierr)
Входные параметры:
- buf - адрес буфера передачи;
- count - количество элементов в буфере передачи;
- datatype - тип данных в буфере передачи;
- op - операция приведения;
- root - ранг главного процесса;
- comm - коммуникатор.
MPI_Reduce применяет операцию приведения к операндам из buf, а результат каждой операции помещается в буфер результата result. MPI_Reduce должна вызываться всеми процессами в коммуникаторе comm, а аргументы count, datatype и op в этих вызовах должны совпадать
Пример
#include "mpi.h" #include <stdio.h> int main(int argc,char *argv[]) { int myrank, i; int count = 5, root = 1; MPI_Group MPI_GROUP_WORLD, subgroup; int ranks[4] = {1, 3, 5, 7}; MPI_Comm subcomm; int sendbuf[5] = {1, 2, 3, 4, 5}; int recvbuf[5]; MPI_Init(&argc, &argv); MPI_Comm_group(MPI_COMM_WORLD, &MPI_GROUP_WORLD); MPI_Group_incl(MPI_GROUP_WORLD, 4, ranks, &subgroup); MPI_Group_rank(subgroup, &myrank); MPI_Comm_create(MPI_COMM_WORLD, subgroup, &subcomm); if(myrank != MPI_UNDEFINED) { MPI_Reduce(&sendbuf, &recvbuf, count, MPI_INT, MPI_SUM, root, subcomm); if(myrank == root) { printf("Reduced values"); for(i = 0; i < count; i++){ printf(" %i ", recvbuf[i]);} } printf("\n"); MPI_Comm_free(&subcomm); MPI_Group_free(&MPI_GROUP_WORLD); MPI_Group_free(&subgroup); } MPI_Finalize(); return 0; }
Пример. Использование операции приведения: вычисление числа методом Монте-Карло
#include <stdlib.h> #include <stdio.h> #include "mpi.h" double dboard (int trials); #define trials 5000 #define ROUNDS 10 #define MASTER 0 main(int argc, char **argv) { double homepi, pisum, pi, avepi; int mytid, nproc, rcode, ; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &mytid); MPI_Comm_size(MPI_COMM_WORLD, &nproc); printf ("MPI task ID = %d\n", mytid); srandom (mytid); avepi = 0; for (i = 0; i < ROUNDS; i++) { homepi = dboard(trials); rcode = MPI_Reduce(&homepi, &pisum, 1, MPI_DOUBLE, MPI_SUM, MASTER, MPI_COMM_WORLD); if (rcode != 0) printf("%d: failure on MPI_Reduce\n", mytid); if (mytid == MASTER) { pi = pisum/nproc; avepi = ((avepi * i) + pi)/(i + 1); printf(" After %3d throws, average value of pi = %10.8f\n", (trials * (i + 1)),avepi); } } MPI_Finalize(); } #include <stdlib.h> #define sqr(x) ((x)*(x)) long random(void); double dboard(int trials) { double x_coord, y_coord, pi, r; int score, n; unsigned long cconst; cconst = 2 << (31 - 1); score = 0; for (n = 1; n <= trials; n++) { r = (double)random()/cconst; x_coord = (2.0 * r) - 1.0; r = (double)random()/cconst; y_coord = (2.0 * r) - 1.0; if ((sqr(x_coord) + sqr(y_coord)) <= 1.0) score++; } pi = 4.0 * (double)score/(double)trials; return(pi); }
Синхронизация
Синхронизация с помощью "барьера" выполняется с помощью подпрограммы:
int MPI_Barrier(MPI_Comm comm) MPI_Barrier(comm, ierr)
Синхронизация с помощью "барьера" - простейшая форма синхронизации коллективных обменов. Она не требует пересылки данных. Обращение к подпрограмме MPI_Barrier блокирует выполнение каждого процесса из коммуникатора comm до тех пор, пока все процессы не вызовут эту подпрограмму.
В общем случае барьер характеризуется "толщиной". "Толщина" – количество процессов, которые должны обратиться к подпрограмме барьерной синхронизации для того, чтобы барьер был преодолен.
Векторная операция распределения данных
Векторная подпрограмма распределения данных:
int MPI_Scatterv(void *sendbuf, int *sendcounts, int *displs, MPI_Datatype sendtype, void *rcvbuf, int rcvcount, MPI_Datatype rcvtype, int root, MPI_Comm comm) MPI_Scatterv(sendbuf, sendcounts, displs, sendtype, rcvbuf, rcvcount, rcvtype, root, comm, ierr)
Входные параметры:
- sendbuf - адрес буфера передачи;
- sendcounts - целочисленный одномерный массив, содержащий количество элементов, передаваемых каждому процессу (индекс равен рангу адресата). Его длина равна количеству процессов в коммуникаторе;
- displs - целочисленный массив, длина которого равна количеству процессов в коммуникаторе. Элемент с индексом i задает смещение относительно начала буфера передачи. Ранг адресата равен значению индекса i;
- sendtype - тип данных в буфере передачи;
- rcvcount - количество элементов в буфере приема;
- rcvtype - тип данных в буфере приема;
- root - ранг передающего процесса;
- comm - коммуникатор.
Выходной параметр: rcvbuf - адрес буфера приема.
Векторная операция сбора данных
Сбор данных от всех процессов в заданном коммуникаторе и запись их в буфер приема с указанным смещением:
int MPI_Gatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int *recvcounts, int *displs, MPI_Datatype recvtype, int root, MPI_Comm comm) MPI_Gatherv(sendbuf, sendcount, sendtype, recvbuf, recvcounts, displs, recvtype, root, comm, ierr)
Список параметров у этой подпрограммы похож на список параметров подпрограммы MPI_Scatterv. В обменах, выполняемых подпрограммами MPI_Allgather и MPI_Alltoall, нет главного процесса. Детали отправки и приема важны для всех процессов, участвующих в обмене.