Санкт-Петербургский государственный университет
Опубликован: 11.10.2012 | Доступ: свободный | Студентов: 955 / 173 | Длительность: 05:14:00
Лекция 5:

Коллективные обмены

< Лекция 4 || Лекция 5: 123 || Лекция 6 >

Операции приведения

Операция приведения (редукции)

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

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;
}
 
      
    
Предопределенные операции приведения

Предопределенные операции приведения

Пример. Использование операции приведения: вычисление числа\pi методом Монте-Карло

#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, нет главного процесса. Детали отправки и приема важны для всех процессов, участвующих в обмене.

< Лекция 4 || Лекция 5: 123 || Лекция 6 >