Вы находитесь на странице: 1из 7

ТЕХНОЛОГИЯ ПАРАЛЛЕЛЬНОГО ПРОГРАММИРОВАНИЯ MPI

Лекция 3. Коммуникаторы. Виртуальная топология.

MPI предоставляет механизм работы с группами процессов и коммуникаторами,


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

Группы

Группа – упорядоченное множество процессов. Каждому процессу в группе поставлено в


соответствие некоторое неотрицательное число – номер процесса. Группы имеют
предопределенный тип MPI_Group. Новые группы можно создавать как на основе других групп,
так и на основе коммуникаторов. Важно: в операциях обменов используются только
коммуникаторы!
Базовая группа, но основе которой могут быть созданы другие группы, связана с
коммуникатором MPI_COMM_WORLD – в нее входят все процессы приложения. Операции над
группами процессов являются локальными. Для их реализации не требуется никаких
межпроцессных обменов – выполняются они только вызвавшим их процессом. Любой процесс
может производить операции над любыми группами, даже с теми, в которые он не входит. При
операциях над группами может получиться пустая группа. Для ее обозначения используется
предопределение MPI_GROUP_EMPTY.

Операции над группами.

1) Функция MPI_Comm_group позволяет получить группу, ассоциированную с


коммуникатором comm. Создание пользовательских групп процессов всегда начинается с
вызова этой функции.
int MPI_Comm_group (MPI_Comm comm., MPI_Group *group);

2) Функция MPI_Group_union позволяет получить новую группу, как объединение двух


существующих групп. Получаемая группа содержит все процессы, входящие в группу group1,
причем в том же самом порядке, а также все процессы группы group2, не входящие в группу
group1, с сохранением их порядка.
int MPI_Group_union (MPI_Group *group1, MPI_Group *group2, MPI_Group *newgroup);
Например: {0,1,3,5,7}{1,3,4,8}={0,1,3,5,7,4,8}

3) Функция MPI_Group_intersection позволяет получить новую группу, как пересечение


двух существующих групп. Получаемая группа содержит все процессы, входящие

1
одновременно в группу group1 и группу group2. Порядок процессов соответствует их порядку в
группе group1.
int MPI_Group_intersection (MPI_Group *group1, MPI_Group *group2,
MPI_Group *newgroup);
Например: {0,1,3,5,7}{3,1,4,7}={1,3,7}

4) Функция MPI_Group_difference позволяет получить новую группу, как разность двух


существующих групп. Получаемая группа содержит все процессы, входящие в группу group1, и
не входящие в группу group2. Порядок процессов соответствует их порядку в группе group1.
int MPI_Group_union (MPI_Group *group1, MPI_Group *group2, MPI_Group *newgroup);
Например: {0,1,3,5,7}{1,3,4,8}={0,5,7}

5) Функция MPI_Group_incl позволяет получить новую группу на основе существующей


группы и заданного числа и последовательности ее процессов.
int MPI_Group_incl (MPI_Group group, int n, int *ranks, MPI_Group *newgroup);
Аргументы:
ranks – номера процессов группы group, которые должны составить новую группу,
n – кол-во процессов в массиве ranks.
При n=0 создается пустая группа MPI_GROUP_EMPTY. Если группу создает процесс, не
входящий в эту группу, то значение новой группы устанавливается в MPI_GROUP_NULL.
Данная функция может быть использована для переопределения порядка процессов в
текущей группе.

6) Функция MPI_Group_excl создает новую группу на основе существующей группы,


исключая из нее заданные процессы.
int MPI_Group_excl (MPI_Group group, int n, int *ranks, MPI_Group *newgroup);
Аргументы:
ranks – номера процессов группы group, которые не должны входить в новую группу,
n – кол-во процессов в массиве ranks.
При n=0 создается группа идентичная заданной. Если группу создает процесс, не
входящий в эту группу, то значение новой группы устанавливается в MPI_GROUP_NULL.

7) Функция MPI_Group_free помечает объект group к удалению. Значение группы


устанавливается в MPI_GROUP_NULL.
int MPI_Group_free (MPI_Group *group);

8) Функция MPI_Group_size позволяет определить размер созданной группы.


int MPI_Group_size (MPI_Group group, int *size);

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


группе.
int MPI_Group_rank (MPI_Group group, int *rank);
Если процесс не принадлежит группе, то значение rank устанавливается в
MPI_UNDEFINED.

2
10) Функция MPI_Group_translate_ranks позволяет определить номеров процессов в
разных группах.
int MPI_Group_translate_ranks (MPI_Group group1, int n, int *ranks1,
MPI_Group group2, int *ranks2);
Функция определяет для процессов группы group1, перечислениых в массиве ranks1, их
номера в группе group2. Если процесс не принадлежит группе, то cзначение rank
устанавливается в MPI_UNDEFINED.

11) Функция MPI_Group_compare позволяет сравнить две группы.


int MPI_Group_compare (MPI_Group group1, MPI_Group group2, int *result);
Если группы абсолютно одинаковы, включая порядок процессов, то в result возвращается
MPI_IDENT. Если одинаковы, но порядок процессов разный, то возвращается MPI_SIMILAR.
Во всех остальных случаях возвращается MPI_UNEQUAL.

Пример:
Разбиваем исходное множество процессов на две равные группы, определяем соответствие
номеров процессов в новых группах их номерам в исходной группе для реализации
взаимодействия между процессами новых групп, имеющих одинаковые номера.
MPI_Group group, group1, group2;
int size, subsize;
int ranks[100], rank, rank1, rank2, rank3;
int i;
int a[4], b[4];
MPI_Status status;

MPI_Init (&argc, &argv);


MPI_Comm_size (MPI_COMM_WORLD, &size);
MPI_Comm_rank (MPI_COMM_WORLD, &rank);
subsize=size/2;
for (i=0; i<subsize; i++) ranks[i]=i;
MPI_Comm_group (MPI_COMM_WORLD, &group);
MPI_Group_incl (group, subsize, ranks, &group1);
MPI_Group_excl (group, subsize, ranks, &group2);
MPI_Group_rank (group1, &rank1);
MPI_Group_rank (group2, &rank2);
if (rank1 == MPI_UNDEFINED)
{
if (rank2<subsize)
{
MPI_Group_translate_ranks (group1, 1, &rank2, group, &rank3);
}
else rank3=MPI_UNDEFINED;
}
else
{
3
MPI_Group_translate_ranks (group2, 1, &rank1, group, &rank3);
}
a[0]=rank;
a[1]=rank1;
a[2]=rank2;
a[3]=rank3;
if (rank3!=MPI_UNDEFINED)
{
MPI_Sendrecv (a, 4, MPI_INT, rank3, 1, b, 4, MPI_INT, rank3, 1, MPI_COMM_WORLD,
&status);
}

Коммуникаторы

Коммуникатор представляет собой некоторый контекст (коммуникационная среда), в


рамках которого производится взаимодействие процессов соответствующей группы. Каждой
группе может соответствовать несколько коммуникаторов. Одному коммуникатору
единовременно соответствует только одна группа.
В отличие от операций над группами, создание коммуникатора является коллективной
операцией и требует межпроцессного обмена. Операции создания коммуникатора должны
вызываться всеми процессами исходного коммуникатора, на базе которого создается новый
коммуникатор, даже теми процессами, которые в него не входят. В противном случае операция
создания коммуникатора будет считаться ошибочной.

Операции над коммуникаторами.

1) Функция MPI_Comm_create создает в рамках некоторого коммуникатора новый


коммуникатор, соответствующий заданной группе.
int MPI_Comm_create (MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm);
Данная функция должна быть вызвана всеми процессами исходного коммуникатора comm.
Для тех процессов, которые не входят в заданную группу group, будет возвращено значение
newcomm=MPI_COMM_NULL.

2) Функция MPI_Comm_dup дублирует существующий коммуникатор.


int MPI_Comm_dup (MPI_Comm comm, MPI_Comm *newcomm);

3) Функция MPI_Comm_compare сравнивает два коммуникатора.


int MPI_Comm_compare (MPI_Comm comm1, MPI_Comm comm2, int *result);
Если коммуникаторы абсолютно одинаковы, включая их контекст, то в result возвращается
MPI_IDENT. Если контекст разный, а группы идентичны, то возвращается MPI_CONGRUENT.
Если отличаются группы коммуникаторов с точки зрения порядка процессов, то возвращается
MPI_SIMILAR. Во всех остальных случаях возвращается MPI_UNEQUAL.

4) Функция MPI_Comm_split осуществляет разбиение исходного коммуникатора на


несколько непересекающихся коммуникаторов.
4
int MPI_Comm_split (MPI_Comm comm, int color, int key, MPI_Comm *newcomm);
Распределение процессов по коммуникаторам производится по числу color – процессы,
вызвавшие функцию с одинаковым значением переменной color, будут принадлежать одному и
тому же коммуникатору. С помощью аргумента key регулируется порядок процессов в
коммуникаторе – чем больше значение key, тем больший номер получит процесс в новом
коммуникаторе. Если текущий процесс передаст в функцию значение color=MPI_UNDEFINED,
то он при разбиении не будет включен ни в один из новых коммуникаторов и в качестве
значения newcomm ему будет возвращено MPI_COMM_NULL.

5) Функция MPI_Comm_free помечает объект comm к удалению. Значение коммуникатора


устанавливается в MPI_COMM_NULL.
int MPI_Comm_free (MPI_Comm *comm);

Пример:
Разбиваем исходное множество процессов на две группы, создаем соответствующие
коммуникаторы, выполняем коллективные операции.
MPI_Group group, group1, group2;
int size, subsize;
int ranks[100], rank, rank1, rank2;
int i;
int a, b;

MPI_Init (&argc, &argv);


MPI_Comm_size (MPI_COMM_WORLD, &size);
MPI_Comm_rank (MPI_COMM_WORLD, &rank);
subsize=size/2;
for (i=0; i<subsize; i++) ranks[i]=i;
MPI_Comm_group (MPI_COMM_WORLD, &group);
MPI_Group_incl (group, subsize, ranks, &group1);
MPI_Group_excl (group, subsize, ranks, &group2);
MPI_Comm_create (MPI_COMM_WORLD, group1, comm1);
MPI_Comm_create (MPI_COMM_WORLD, group2, comm2);
if (comm1 != MPI_COMM_NULL)
{
MPI_Comm_rank (comm1, &rank1);
MPI_Reduce (&a, &b, 10, MPI_INT, MPI_SUM, 0, comm1);
if (rank1==0) printf (“Process %d: Sum[a]=%d\n”, rank, b);
}
if (comm2 != MPI_COMM_NULL)
{
MPI_Comm_rank (comm2, &rank2);
MPI_Reduce (&a, &b, 10, MPI_INT, MPI_MAX, 0, comm2);
if (rank2==0) printf (“Process %d: Max[a]=%d\n”, rank, b);
}
MPI_Comm_free (&comm1);
5
MPI_Comm_free (&comm2);
MPI_Group_free (&group);
MPI_Group_free (&group1);
MPI_Group_free (&group2);

Виртуальная топология

В MPI предусмотрен механизм создания виртуальной топологии процессов. Топология


используется для более удобного обозначения процессов с целью приближения структуры
программы к структуре заложенного в ней математического алгоритма. Кроме этого топология
может использоваться системой для оптимизации распределения процессов по физическим
процессорам.
Определены два типа топологии: декартова топология и топология графа.

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

1) Создание декартовой топологии


int MPI_Cart_create (MPI_Comm comm, int ndims, int *dims, int *periods, int reorder,
MPI_Comm *cartcomm);
Функция создает коммуникатор cartcomm с декартовой топологией на базе коммуникатора
comm. Аргумент ndims задает размерность решетки, а массив dims ее размеры. Массив periods
определяет периодичность каждого из направлений решетки (1-направление периодично, 0 -
нет). Параметр reorder, установленный в 1 позволяет системе менять порядок нумерации
процессов для оптимизации распределения процессов по физическим процессорам.
Функция создания декартовой топологии является коллективной. Если кол-во процессов в
исходном коммуникаторе больше, чем необходимо для топологии, то тем процессам, которые в
нее не войдут, будет возвращено MPI_COMM_NULL. Если кол-ва процессов для создания
топологии недостаточно, то будет возвращена ошибка.

2) Определение размеров топологии


int MPI_Dims_create (int nnodes, int ndims, int *dims);
Функция позволяет пользователю определить сбалансированные размеры решетки из
nnodes процессов перед ее созданием. Пользователь может управлять размерами решетки,
передавая в соответствующих позициях массива dims требуемое значение размера. Если размер
по направлению должен быть рассчитан, то соответствующее значение элемента массива dims
должно быть равно 0.

3) Определение координат процесса


int MPI_Cart_coords (MPI_Comm cartcomm, int rank, int ndims, int *coords);
Функция позволяет пользователю определить координаты coords процесса rank в
топологии cartcomm.
int MPI_Cart_rank (MPI_Comm cartcomm, int *cords, int *rank);
Функция позволяет пользователю определить по координатам coords номер процесса rank
в топологии cartcomm.
6
4) Определение коммуникаторов для направлений решетки
int MPI_Cart_sub (MPI_Comm cartcomm, int *remain_dims, MPI_Comm *newcomm);
Функция расщепляет коммуникатор cartcomm на подгруппы, соответствующие
декартовым подрешеткам меньшей размерности. Массив remain_dims определяет формат
подрешетки. Если некоторое измерение должно остаться в формируемой подрешетке, то
соответствующее значение массива должно быть установлено в 1, в противном случае в 0.

5) Определение соседних процессов по направлениям решетки


int MPI_Cart_shift (MPI_Comm cartcomm, int direction, int disp,
int *rank_source, int *rank_dest);
Функция определяет номера процессов, соседних с текущим процессов по направлению
direction со сдвигом disp.

Топология графа

1) Создание топологии графа


int MPI_Graph_create (MPI_Comm comm, int nnodes, int *index, int *edges, int reorder,
MPI_Comm *graphcomm);
Функция создает коммуникатор graphcomm с топологией графа на базе коммуникатора
comm. Параметр nnodes задает число вершин в графе. Элемент i массива index равен
суммарному кол-ву смежных вершин графа для первых i его вершин. Массив edges содержит
упорядоченные списки смежных вершин. Параметр reorder, установленный в 1 позволяет
системе менять порядок нумерации процессов для оптимизации распределения процессов по
физическим процессорам.
Функция создания топологии графа является коллективной. Если кол-во процессов в
исходном коммуникаторе больше, чем необходимо для топологии, то тем процессам, которые в
нее не войдут, будет возвращено MPI_COMM_NULL. Если кол-ва процессов для создания
топологии недостаточно, то будет возвращена ошибка.

2) Определение кол-ва процессов-соседей


int MPI_Graph_neighbors_count (MPI_Comm comm, int rank, int *neighbors);
Функция определяет кол-во процессов, непосредственно связанных с процессом rank в
рамках топологии графа.

3) Определение номеров процессов-соседей


int MPI_Graph_neighbors (MPI_Comm comm, int rank, int maxneighbors,
int *neighbors);
Функция определяет номера процессов, непосредственно связанных с процессом rank в
рамках топологии графа.