Обмениваются группы 0 и 1, затем группы 1 и 2. Поэтому, группа 0 требует один интер-коммуникатор, группа 1 требует два интер-коммуникатора и группа 2 требует 1 интер-коммуникатор.
main(int argc, char **argv)
{
MPI_Comm myComm; /* интракоммуникатор локальной
подгруппы */
MPI_Comm myFirstComm; /* интеркоммуникатор */
MPI_Comm mySecondComm; /* второй интеркоммуникатор
(только группа 1) */
int membershipKey;
int rank;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
/* код пользователя обязан генерировать ключ
принадлежности в диапазоне [0, 1, 2] */
membershipKey = rank % 3;
/* формирование интра-коммуникатора для локальной подгруппы */
MPI_Comm_split(MPI_COMM_WORLD, membershipKey, rank, &myComm);
/* формирование интер-коммуникаторов
*/
if (membershipKey == 0)
{ /* Группа 0 связывается с группой 1 */
MPI_Intercomm_create(myComm, 0,
MPI_COMM_WORLD, 1, 1, &myFirstComm);
}
else if (membershipKey == 1)
{ /* Группа 1 связывается с группами 0 и 2. */
MPI_Intercomm_create(myComm, 0, MPI_COMM_WORLD,
0, 1, &myFirstComm);
MPI_Intercomm_create(myComm, 0, MPI_COMM_WORLD,
2, 12, &mySecondComm);
}
else if (membershipKey == 2)
{ /* Группа 2 связывается с группой 1. */
MPI_Intercomm_create(myComm, 0, MPI_COMM_WORLD,
1, 12, &myFirstComm);
}
/* рабочий участок ... */
switch(membershipKey) /* удаление коммуникаторов
*/
{
case 1:
MPI_Comm_free(&mySecondComm);
case 0:
case 2:
MPI_Comm_free(&myFirstComm);
break;
}
MPI_Finalize();
}
Пример 2: Кольцо из трех групп
Обмениваются группы 0 и 1, группы 1 и 2, группы 0 и 2. Следовательно, каждая группа требует два интеркоммуникатора.
main(int argc, char **argv)
{
MPI_Comm myComm; /* интра-коммуникатор для локальной
подгруппы */
MPI_Comm myFirstComm; /* интер-коммуникатор */
MPI_Comm mySecondComm;
MPI_Status status;
int membershipKey;
int rank;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
...
/* код пользователя должен генерировать ключ принадлежности в
диапазоне [0, 1, 2] */
membershipKey = rank % 3;
/* формирование интра-коммуникатора для локальной подгруппы */
MPI_Comm_split(MPI_COMM_WORLD, membershipKey, rank, &myComm);
/* формирование интер-коммуникаторов. */
if (membershipKey == 0)
{ /* Группа 0 связывается с группами 1 и 2. */
MPI_Intercomm_create(myComm, 0, MPI_COMM_WORLD, 1,
1, &myFirstComm);
MPI_Intercomm_create(myComm, 0, MPI_COMM_WORLD, 2,
2, &mySecondComm);
}
else if (membershipKey == 1)
{ /* Группа 1 связывается с группами 0 и 2. */
MPI_Intercomm_create(myComm, 0, MPI_COMM_WORLD, 0,
1, &myFirstComm);
MPI_Intercomm_create(myComm, 0, MPI_COMM_WORLD, 2,
12, &mySecondComm);
}
else if (membershipKey == 2)
{ /* Группа 1 связывается с группами 0 и 1. */
MPI_Intercomm_create(myComm, 0, MPI_COMM_WORLD, 0,
2, &myFirstComm);
MPI_Intercomm_create(myComm, 0, MPI_COMM_WORLD, 1,
12, &mySecondComm);
}
/* выполнение некоторой работы ... */
/* Теперь освобождаем коммуникаторы перед окончанием... */
MPI_Comm_free(&myFirstComm);
MPI_Comm_free(&mySecondComm);
MPI_Comm_free(&myComm);
MPI_Finalize();
}
Пример 3: Формирование службы имен для интер-коммуникации
Следующие подпрограммы показывают, как пользователь может создавать службу имен (name service) для интер-коммуникаторов на основе протокола рандеву (rendezvous) с использованием серверного коммуникатора и имени тэга, выбранного обеими группами.
После того, как все процессы MPI выполнят MPI_INIT, каждый процесс вызывает функцию Init_server(), определенную ниже. Тогда, если new_world возвращает NULL, необходим процесс, получающий NULL, чтобы осуществить функцию сервера в цикле Do_server(). Все остальные процессы делают только предписанные им вычисления, используя new_world как новый эффективный ``глобальный'' коммуникатор. Один определенный процесс вызывает Undo_Server(), чтобы избавиться от сервера, когда он больше не требуется.
Особенности этого подхода включают:
#define INIT_SERVER_TAG_1 666
#define UNDO_SERVER_TAG_1 777
static int server_key_val;
/* для управления атрибутами на server_comm
используется функция копирования: */
void handle_copy_fn(MPI_Comm *oldcomm, int *keyval,
void *extra_state, void *attribute_val_in,
void **attribute_val_out, int *flag)
{
/* копирование дескриптора */
*attribute_val_out = attribute_val_in;
*flag = 1; /* указывает, что копирование произошло */
}
int Init_server(peer_comm, rank_of_server,
server_comm, new_world)
MPI_Comm peer_comm;
int rank_of_server;
MPI_Comm *server_comm;
MPI_Comm *new_world;
{
MPI_Comm temp_comm, lone_comm;
MPI_Group peer_group, temp_group;
int rank_in_peer_comm, size, color, key = 0;
int peer_leader, peer_leader_rank_in_temp_comm;
MPI_Comm_rank(peer_comm, &rank_in_peer_comm);
MPI_Comm_size(peer_comm, &size);
if ((size < 2)||(0 > rank_of_server)
||(rank_of_server >= size))
return (MPI_ERR_OTHER);
/* создаются два коммуникатора путем разбиния peer_comm на
процесс сервера и какой-либо еще */
/* произвольный выбор */
peer_leader = (rank_of_server + 1) % size;
if ((color = (rank_in_peer_comm == rank_of_server)))
{
MPI_Comm_split(peer_comm, color, key, &lone_comm);
MPI_Intercomm_create(lone_comm, 0, peer_comm,
peer_leader, INIT_SERVER_TAG_1, server_comm);
MPI_Comm_free(&lone_comm);
*new_world = MPI_COMM_NULL;
}
else
{
MPI_Comm_Split(peer_comm, color, key, &temp_comm);
MPI_Comm_group(peer_comm, &peer_group);
MPI_Comm_group(temp_comm, &temp_group);
MPI_Group_translate_ranks(peer_group, 1, &peer_leader,
temp_group, &peer_leader_rank_in_temp_comm);
MPI_Intercomm_create(temp_comm,
peer_leader_rank_in_temp_comm,
peer_comm, rank_of_server,
INIT_SERVER_TAG_1, server_comm);
/* присоединяется коммуникационный атрибут new_world к
server_comm: */
/* КРИТИЧЕСКАЯ СЕКЦИЯ ДЛЯ MULTITHREADING */
if(server_keyval == MPI_KEYVAL_INVALID)
{
/* получить локальное имя процесса для
значения ключа сервера */
MPI_keyval_create(handle_copy_fn, NULL,
&server_keyval, NULL);
}
*new_world = temp_comm;
/* кэшировать дескриптор интра-коммуникатора на
интер-коммуникаторе: */
MPI_Attr_put(server_comm, server_keyval, (void*)
(*new_world));
}
return (MPI_SUCCESS);
}
Фактический процесс сервера передал бы на выполнение следующий код:
int Do_server(server_comm)
MPI_Comm server_comm;
{
void init_queue();
int en_queue(), de_queue();
/* Сохранить триплеты целых чисел для
последующего cравнения */
MPI_Comm comm;
MPI_Status status;
int client_tag, client_source;
int client_rank_in_new_world,
pairs_rank_in_new_world;
int buffer[10], count = 1;
void *queue;
init_queue(&queue);
for (;;)
{
MPI_Recv(buffer, count, MPI_INT, MPI_ANY_SOURCE,
MPI_ANY_TAG, server_comm, &status);
/* прием от любого клиента */
/* Определяется клиент: */
client_tag = status.MPI_TAG;
client_source = status.MPI_SOURCE;
client_rank_in_new_world = buffer[0];
if (client_tag == UNDO_SERVER_TAG_1)
/* Клиент, который завершает работу сервера */
{
while(de_queue(queue,MPI_ANY_TAG,
&pairs_rank_in_new_world, &pairs_rank_in_server));
MPI_Intercomm_free(&server_comm);
break;
}
if (de_queue(queue, client_tag, &pairs_rank_in_new_world,
&pairs_rank_in_server))
{
/* согласованная пара с одинаковым тэгом, необходимо
сообщить им друг о друге! */
buffer[0] = pairs_rank_in_new_world;
MPI_Send(buffer, 1, MPI_INT, client_src, client_tag,
server_comm);
buffer[0] = client_rank_in_new_world;
MPI_Send(buffer, 1, MPI_INT, pairs_rank_in_server,
client_tag, server_comm);
}
else
en_queue(queue, client_tag, client_source,
client_rank_in_new_world);
}
}
Особый процесс отвечает за окончание работы сервера, когда он больше не нужен, вызывая Undo_server.
int Undo_server(server_comm)
/* пример клиента, который заканчивает
работу сервера */
MPI_Comm *server_comm;
{
int buffer = 0;
MPI_Send(&buffer,1, MPI_INT, 0, UNDO_SERVER_TAG_1, *server_comm);
MPI_Intercomm_free(server_comm);
}
Следующий код - это код блокирования сервисов имен для интер-коммуникации с теми же самыми семантическими ограничениями, как в MPI_Intercomm_create, но с упрощенным синтаксисом. Он использует функциональные возможности, определенные только для создания службы имен.
int Intercomm_name_create(local_comm, server_comm, tag, comm)
MPI_Comm local_comm, server_comm;
int tag;
MPI_Comm *comm;
{
int error;
int found; /* получение атрибута mgmt для new_world */
void *val;
MPI_Comm new_world;
int buffer[10], rank;
int local_leader = 0;
MPI_Attr_get(server_comm, server_keyval,
&val, &found);
new_world = (MPI_Comm)val;
/* восстановление кэшированного
дескриптора */
MPI_Comm_rank(server_comm, &rank);
/* номер в локальной группе */
if (rank == local_leader)
{
buffer[0] = rank;
MPI_Send(&buffer, 1, MPI_INT, 0, tag, server_comm);
MPI_Recv(&buffer, 1, MPI_INT, 0, tag, server_comm);
}
error = MPI_Intercomm_create(local_comm, local_leader, new_world,
buffer[0], tag, comm);
return(error);
}