В данном подразделе рассматриваются функции работы с коммуникаторами. Они разделяются на функции доступа к коммуникаторам и функции создания коммуникаторов. Функции доступа являются локальными и не требуют коммуникаций, в отличие от функций создания, которые являются коллективными и могут потребовать межпроцессорных коммуникаций.
Две основных функции доступа к коммуникатору, MPI_Comm_size - опрос числа процессов в области связи и MPI_Comm_rank - опрос идентификатора номера процесса в области связи, были рассмотрены в самом начале среди базовых функций MPI.
Кроме них, имеется функция сравнения двух коммуникаторов MPI_Comm_compare.
Возможные значения результата сравнения:
MPI_IDENT | - | коммуникаторы идентичны, представляют один и тот же объект; |
MPI_CONGRUENT | - | коммуникаторы конгруэнтны, две области связи с одними и теми же атрибутами группы; |
MPI_SIMILAR | - | коммуникаторы подобны, группы содержат одни и те же процессы, но другое упорядочивание; |
MPI_UNEQUAL | - | во всех других случаях. |
Создание нового коммуникатора возможно с помощью одной из трех функций: MPI_Comm_dup, MPI_Comm_create, MPI_Comm_split.
Функция дублирования коммуникатора MPI_Comm_dup
Функция полезна для последующего создания коммуникаторов с новыми атрибутами.
Функция создания коммуникатора MPI_Comm_create
Эта функция создает коммуникатор для группы group. Для процессов, которые не являются членами группы, возвращается значение MPI_COMM_NULL. Функция возвращает код ошибки, если группа group не является подгруппой родительского коммуникатора.
Функция расщепления коммуникатора MPI_Comm_split
Функция расщепляет группу, связанную с родительским коммуникатором, на непересекающиеся подгруппы по одной на каждое значение признака подгруппы color. Значение color должно быть неотрицательным. Каждая подгруппа содержит процессы с одним и тем же значением color. Параметр key управляет упорядочиванием внутри новых групп: меньшему значению key соответствует меньшее значение идентификатора процесса. В случае равенства параметра key для нескольких процессов упорядочивание выполняется в соответствии с порядком в родительской группе.
Приведем алгоритм расщепления группы из восьми процессов на три подгруппы и его графическую интерпретацию (Рис. 4.18).
MPI_comm comm, newcomm; int myid, color; . . . . . . MPI_Comm_rank(comm, &myid); color = myid%3;
MPI_Comm_split(comm, color, myid, &newcomm);
Рис. 4.18 Разбиение группы из восьми процессов на три подгруппы.
В данном примере первую подгруппу образовали процессы, номера которых делятся на 3 без остатка, вторую - для которых остаток равен 1 и третью - для которых остаток равен 2. Отметим, что после выполнения функции MPI_Comm_split значения коммуникатора newcomm в процессах разных подгрупп будут отличаться.
Функция уничтожения коммуникатораа MPI_Comm_free
Примечание: За рамками данного руководства мы оставим обсуждение inter-коммуникаторов и вопросы, связанные с изменением или добавлением новых атрибутов коммуникаторов.
В заключение настоящего пункта приведем пример программы, иллюстрирующую работу с группами и коммуникаторами.
Программа group.c
#include "mpi.h" #include <stdio.h> #define NPROCS 8 int main(argc,argv) int argc; char *argv[]; { int rank, new_rank, sendbuf, recvbuf, numtasks, ranks1[4]={0,1,2,3}, ranks2[4]={4,5,6,7}; MPI_Group orig_group, new_group; MPI_Comm new_comm; MPI_Init(&argc,&argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &numtasks); if (numtasks != NPROCS) { printf("Must specify MP_PROCS= %d. Terminating.\n",NPROCS); MPI_Finalize(); exit(0); } sendbuf = rank; MPI_Comm_group(MPI_COMM_WORLD, &orig_group); if (rank < NPROCS/2) { MPI_Group_incl(orig_group, NPROCS/2, ranks1, &new_group); } else { MPI_Group_incl(orig_group, NPROCS/2, ranks2, &new_group); } MPI_Comm_create(MPI_COMM_WORLD, new_group, &new_comm); MPI_Allreduce(&sendbuf,&recvbuf,1,MPI_INT,MPI_SUM,new_comm); MPI_Group_rank (new_group, &new_rank); printf("rank=%dnewrank=%drecvbuf=%d\n", rank,new_rank,recvbuf); MPI_Finalize(); }
Программа group.f
program group include 'mpif.h' integer NPROCS parameter(NPROCS=8) integer rank, new_rank, sendbuf, recvbuf, numtasks integer ranks1(4), ranks2(4), ierr integer orig_group, new_group, new_comm data ranks1 /0, 1, 2, 3/, ranks2 /4, 5, 6, 7/ call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD, numtasks, ierr) if (numtasks .ne. NPROCS) then print *, 'Must specify MPROCS= ',NPROCS,'Terminating.' call MPI_FINALIZE(ierr) stop endif sendbuf = rank call MPI_COMM_GROUP(MPI_COMM_WORLD, orig_group, ierr) if (rank .lt. NPROCS/2) then call MPI_GROUP_INCL(orig_group, NPROCS/2, ranks1, & new_group, ierr) else call MPI_GROUP_INCL(orig_group, NPROCS/2, ranks2, & new_group, ierr) endif call MPI_COMM_CREATE(MPI_COMM_WORLD, new_group, & new_comm, ierr) call MPI_ALLREDUCE(sendbuf, recvbuf, 1, MPI_INTEGER, & MPI_SUM, new_comm, ierr) call MPI_GROUP_RANK(new_group, new_rank, ierr) print*, 'rank= ',rank,' newrank= ',new_rank,'recvbuf=', & recvbuf call MPI_FINALIZE(ierr) end
Результат работы:
rank= 0 newrank= 0 recvbuf= 6 rank= 4 newrank= 0 recvbuf= 22 rank= 6 newrank= 2 recvbuf= 22 rank= 2 newrank= 2 recvbuf= 6 rank= 7 newrank= 3 recvbuf= 22 rank= 3 newrank= 3 recvbuf= 6 rank= 5 newrank= 1 recvbuf= 22 rank= 1 newrank= 1 recvbuf= 6