| |
Обмениваются группы 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); }
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |