Вперед: 4.8. Примеры разработки типовых программ
Назад: 4.7.1. Основные понятия
К содержанию: Оглавление


4.7.2. Декартова топология

Обобщением линейной и матричной топологий на произвольное число измерений является декартова топология. Для создания коммуникатора с декартовой топологией используется функция MPI_Cart_create. С помощью этой функции можно создавать топологии с произвольным числом измерений, причем по каждому измерению в отдельности можно накладывать периодические граничные условия. Таким образом, для одномерной топологии мы можем получить или линейную структуру, или кольцо в зависимости от того, какие граничные условия будут наложены. Для двумерной топологии, соответственно, либо прямоугольник, либо цилиндр, либо тор. Заметим, что не требуется специальной поддержки гиперкубовой структуры, поскольку она представляет собой n-мерный тор с двумя процессами вдоль каждого координатного направления.

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

С:
MPI_Cart_create(MPI_Comm comm_old, int ndims, int *dims,
int *periods, int reorder, MPI_Comm *comm_cart)
FORTRAN:
MPI_CART_CREATE(COMM_OLD, NDIMS, DIMS, PERIODS,
REORDER, COMM_CART, IERROR)
INTEGER COMM_OLD, NDIMS, DIMS(*), COMM_CART, IERROR
LOGICAL PERIODS(*), REORDER
IN comm_old - родительский коммуникатор;
IN ndims - число измерений;
IN dims - массив размера ndims, в котором задается число процессов вдоль каждого измерения;
IN periods - логический массив размера ndims для задания граничных условий (true - периодические, false - непериодические);
IN reorder - логическая переменная, указывает, производить перенумерацию процессов (true) или нет (false);
OUT comm_cart - новый коммуникатор.

Функция является коллективной, т.е. должна запускаться на всех процессах, входящих в группу коммуникатора comm_old. При этом, если какие-то процессы не попадают в новую группу, то для них возвращается результат MPI_COMM_NULL. В случае, когда размеры заказываемой сетки больше имеющегося в группе числа процессов, функция завершается аварийно. Значение параметра reorder=false означает, что идентификаторы всех процессов в новой группе будут такими же, как в старой группе. Если reorder=true, то MPI будет пытаться перенумеровать их с целью оптимизации коммуникаций.

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

Функция определения оптимальной конфигурации сетки

С:
MPI_Dims_create(int nnodes, int ndims, int *dims)
FORTRAN:
MPI_DIMS_CREATE(NNODES, NDIMS, DIMS, IERROR)
INTEGER NNODES, NDIMS, DIMS(*), IERROR
IN nnodes - общее число узлов в сетке;
IN ndims - число измерений;
INOUT dims - массив целого типа размерности ndims, в который помещается рекомендуемое число процессов вдоль каждого измерения.

На входе в процедуру в массив dims должны быть занесены целые неотрицательные числа. Если элементу массива dims[i] присвоено положительное число, то для этой размерности вычисление не производится (число процессов вдоль этого направления считается заданным). Вычисляются только те компоненты dims[i], для которых перед обращением к процедуре были присвоены значения 0. Функция стремится создать максимально равномерное распределение процессов вдоль направлений, выстраивая их по убыванию, т.е. для 12-ти процессов она построит трехмерную сетку 4 х 3 х 1. Результат работы этой процедуры может использоваться в качестве входного параметра для процедуры MPI_Cart_create.

Функция опроса числа измерений декартовой топологии MPI_Cartdim_get

С:
MPI_Cartdim_get(MPI_Comm comm, int *ndims)
FORTRAN:
MPI_CARTDIM_GET(COMM, NDIMS, IERROR)
INTEGER COMM, NDIMS, IERROR
IN comm - коммуникатор с декартовой топологией;
OUT ndim - число измерений в декартовой топологии.

Функция возвращает число измерений в декартовой топологии ndims для коммуникатора comm.

Результат может быть использован в качестве параметра для вызова функции MPI_Cart_get, которая служит для получения более детальной информации.

С:
MPI_Cart_get(MPI_Comm comm, int ndims, int *dims,
int *periods, int *coords)
FORTRAN:
MPI_CART_GET(COMM, NDIMS, DIMS, PERIODS, COORDS, IERROR)
INTEGER COMM, NDIMS, DIMS(*), COORDS(*), IERROR
LOGICAL PERIODS(*)
IN ndims - число измерений;
OUT dims - массив размера ndims, в котором возвращается число процессов вдоль каждого измерения;
OUT periods - логический массив размера ndims, в котором возвращаются наложенные граничные условия; (true - периодические, false - непериодические);
OUT coords - координаты в декартовой сетке вызывающего процесса.

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

Функция получения идентификатора процесса по его координатам MPI_Cart_rank

С:
MPI_Cart_rank(MPI_Comm comm, int *coords, int *rank)
FORTRAN:
MPI_CART_RANK(COMM, COORDS, RANK, IERROR)
INTEGER COMM, COORDS(*), RANK, IERROR
IN comm - коммуникатор с декартовой топологией;
IN coords - координаты в декартовой системе;
OUT rank - идентификатор процесса.

Для измерений с периодическими граничными условиями будет выполняться приведение к основной области определения 0 <= coords(i) < dims(i).

Функция определения координат процесса по его идентификатору MPI_Cart_coords

С:
MPI_Cart_coords(MPI_Comm comm, int rank, int ndims, int *coords)
FORTRAN:
MPI_CART_COORDS(COMM, RANK, NDIMS, COORDS, IERROR)
INTEGER COMM, RANK, NDIMS, COORDS(*), IERROR
IN comm - коммуникатор с декартовой топологией;
IN rank - идентификатор процесса;
IN ndim - число измерений;
OUT coords - координаты процесса в декартовой топологии.

Во многих численных алгоритмах используется операция сдвига данных вдоль каких-то направлений декартовой решетки. В MPI существует специальная функция MPI_Cart_shift, реализующая эту операцию. Точнее говоря, сдвиг данных осуществляется с помощью функции MPI_Sendrecv, а функция MPI_Cart_shift вычисляет для каждого процесса параметры для функции MPI_Sendrecv (source и dest).

Функция сдвига данных MPI_Cart_shift

С:
MPI_Cart_shift(MPI_Comm comm, int direction, int disp,
int *rank_source, int *rank_dest)
FORTRAN:
MPI_CART_SHIFT(COMM, DIRECTION, DISP, RANK_SOURCE,
RANK_DEST, IERROR)
INTEGER COMM, DIRECTION, DISP, RANK_SOURCE,
RANK_DEST, IERROR
IN comm - коммуникатор с декартовой топологией;
IN direction - номер измерения, вдоль которого выполняется сдвиг;
IN disp - величина сдвига (может быть как положительной, так и отрицательной);
OUT rank_sourceа - номер процесса, от которого должны быть получены данные;
OUT rank_dest - номер процесса, которому должны быть посланы данные.

Номер измерения и величина сдвига не обязаны быть одинаковыми для всех процессов. В зависимости от граничных условий сдвиг может быть либо циклический, либо с учетом граничных процессов. В последнем случае для граничных процессов возвращается MPI_PROC_NULL либо для переменной rank_source, либо для rank_dest. Это значение также может быть использовано при обращении к функции MPI_sendrecv.

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

Функция выделения подпространства в декартовой топологии MPI_Cart_sub

С:
MPI_Cart_sub(MPI_Comm comm, int *remain_dims,
MPI_Comm *newcomm)
FORTRAN:
MPI_CART_SUB(COMM, REMAIN_DIMS, NEWCOMM, IERROR)
INTEGER COMM, NEWCOMM, IERROR
LOGICAL REMAIN_DIMS(*)
IN comm - коммуникатор с декартовой топологией;
IN remain_dims - логический массив размера ndims, указывающий, входит ли i-e измерение в новую подрешетку (remain_dims[i] = true);
OUT newcomm - новый коммуникатор, описывающий подрешетку, содержащую вызывающий процесс.

Функция является коллективной. Действие функции проиллюстрируем следующим примером. Предположим, что имеется декартова решетка 2 х 3 х 4, тогда обращение к функции MPI_Cart_sub с массивом remain_dims (true, false, true) создаст три коммуникатора с топологией 2 х 4. Каждый из коммуникаторов будет описывать область связи, состоящую из 1/3 процессов, входивших в исходную область связи.

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

Для определения топологии коммуникатора служит функция MPI_Topo_test.

С:
MPI_Topo_test(MPI_Comm comm, int *status)
FORTRAN:
MPI_TOPO_TEST(COMM, STATUS, IERROR)
INTEGER COMM, STATUS, IERROR
IN comm - коммуникатор;
OUT status - топология коммуникатора.

Функция MPI_Topo_test возвращает через переменную status топологию коммуникатора comm. Возможные значения:
MPI_GRAPH - топология графа;
MPI_CART - декартова топология;
MPI_UNDEFINED - топология не задана.

Приведем пример программы с использованием двумерной декартовой топологии. Из 16 процессоров строится сетка 4х4 и каждый узел обменивается своим номером с ближайшими соседями.

Программа cart.c

 #include "mpi.h"
 #include <stdio.h>
 #define SIZE 16
 #define UP  0
 #define DOWN 1
 #define LEFT 2
 #define RIGHT 3

 int main(argc,argv)
 int argc;
 char *argv[]; {
 int numtasks, rank, source, dest, outbuf, i, tag=1, 
  inbuf[4]={MPI_PROC_NULL,MPI_PROC_NULL,MPI_PROC_NULL,MPI_PROC_NULL,}, 
 nbrs[4], dims[2]={4,4}, 
 periods[2]={0,0}, reorder=0, coords[2];
 MPI_Request reqs[8];
 MPI_Status stats[8];
 MPI_Comm cartcomm;

 MPI_Init(&argc,&argv);
 MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
 if (numtasks == SIZE) {
 MPI_Cart_create(MPI_COMM_WORLD, 2, dims, periods, reorder, &cartcomm);
 MPI_Comm_rank(cartcomm, &rank);
 MPI_Cart_coords(cartcomm, rank, 2, coords);
 MPI_Cart_shift(cartcomm, 0, 1, &nbrs[UP], &nbrs[DOWN]);
 MPI_Cart_shift(cartcomm, 1, 1, &nbrs[LEFT], &nbrs[RIGHT]);
 outbuf = rank;
 for (i=0; i<4; i++) {
   dest = nbrs[i];
   source = nbrs[i];
   MPI_Isend(&outbuf, 1, MPI_INT, dest, tag, 
        MPI_COMM_WORLD, &reqs[i]);
   MPI_Irecv(&inbuf[i], 1, MPI_INT, source, tag, 
        MPI_COMM_WORLD, &reqs[i+4]);
   }
 MPI_Waitall(8, reqs, stats);
 printf("rank= %d coords= %d %d neighbors(u,d,l,r)= %d %d %d %d\n",
    rank,coords[0],coords[1],nbrs[UP],nbrs[DOWN],nbrs[LEFT],
    nbrs[RIGHT]);
 printf("rank= %d  inbuf(u,d,l,r)= %d %d %d %d\n",
    rank,inbuf[UP],inbuf[DOWN],inbuf[LEFT],inbuf[RIGHT]);
 }
 else
 printf("Must specify %d processors. Terminating.\n",SIZE);
  
 MPI_Finalize();
 }

Программа cart.f

 program cart
  include 'mpif.h'
  integer SIZE, UP, DOWN, LEFT, RIGHT
  parameter(SIZE=16)
  parameter(UP=1)
  parameter(DOWN=2)
  parameter(LEFT=3)
  parameter(RIGHT=4)
  integer numtasks,rank,source,dest, outbuf, i, tag, ierr,
 &    inbuf(4), nbrs(4), dims(2), coords(2),
 &    stats(MPI_STATUS_SIZE, 8), reqs(8), cartcomm,
 &    periods(2), reorder
  data inbuf /MPI_PROC_NULL,MPI_PROC_NULL,MPI_PROC_NULL,
 &   MPI_PROC_NULL/, dims /4,4/, tag /1/, 
 &   periods /0,0/, reorder /0/ 
  call MPI_INIT(ierr)
  call MPI_COMM_SIZE(MPI_COMM_WORLD, numtasks, ierr)
 
  if (numtasks .eq. SIZE) then
 call MPI_CART_CREATE(MPI_COMM_WORLD,2,dims,periods, 
 &   reorder, cartcomm, ierr)
  call MPI_COMM_RANK(cartcomm, rank, ierr)
  call MPI_CART_COORDS(cartcomm, rank, 2, coords, ierr)
  print *,'rank= ',rank,'coords= ',coords
  call MPI_CART_SHIFT(cartcomm,0,1,nbrs(UP),nbrs(DOWN), ierr)
   call MPI_CART_SHIFT(cartcomm, 1, 1, nbrs(LEFT), 
 &   nbrs(RIGHT), ierr)
   outbuf = rank
   do i=1,4
     dest = nbrs(i)
     source = nbrs(i)
     call MPI_ISEND(outbuf, 1, MPI_INTEGER, dest, tag,
 &          MPI_COMM_WORLD, reqs(i), ierr)
     call MPI_IRECV(inbuf(i), 1, MPI_INTEGER, source, 
 &      tag, MPI_COMM_WORLD, reqs(i+4), ierr)
   enddo
   call MPI_WAITALL(8, reqs, stats, ierr)
   print *,'rank= ',rank,' coords= ',coords, 
 &      ' neighbors(u,d,l,r)= ',nbrs
   print *,'rank= ',rank,'         ', 
 &      ' inbuf(u,d,l,r)= ',inbuf
  else
   print*,'Must specify',SIZE,' processors. Terminating.' 
  endif
  call MPI_FINALIZE(ierr)
  end

Вывод программы (частичный):

rank= 0 coords= 0 0 neighbors(u,d,l,r)= -3 4 -3 1
rank= 0        inbuf(u,d,l,r)= -3 4 -3 1
rank= 1 coords= 0 1 neighbors(u,d,l,r)= -3 5 0 2
rank= 1        inbuf(u,d,l,r)= -3 5 0 2
rank= 2 coords= 0 2 neighbors(u,d,l,r)= -3 6 1 3
rank= 2        inbuf(u,d,l,r)= -3 6 1 3
    . . . . .

rank= 14 coords= 3 2 neighbors(u,d,l,r)= 10 -3 13 15
rank= 14        inbuf(u,d,l,r)= 10 -3 13 15
rank= 15 coords= 3 3 neighbors(u,d,l,r)= 11 -3 14 -3
rank= 15        inbuf(u,d,l,r)= 11 -3 14 -3


Вперед: 4.8. Примеры разработки типовых программ
Назад: 4.7.1. Основные понятия
К содержанию: Оглавление