Головная программа:
main(int argc, char **argv)
{
int ma, mb;
MPI_Group MPI_GROUP_WORLD, group_a, group_b;
MPI_Comm comm_a, comm_b;
static int list_a[] = {0, 1};
#if defined(EXAMPLE_2B) | defined(EXAMPLE_2C)
static int list_b[] = {0, 2 ,3};
#else/* EXAMPLE_2A */
static int list_b[] = {0, 2};
#endif
int size_list_a = sizeof(list_a)/sizeof(int);
int size_list_b = sizeof(list_b)/sizeof(int);
...
MPI_Init(&argc, &argv);
MPI_Comm_group(MPI_COMM_WORLD, &MPI_GROUP_WORLD);
MPI_Group_incl(MPI_GROUP_WORLD, size_list_a, list_a, &group_a);
MPI_Group_incl(MPI_GROUP_WORLD, size_list_b, list_b, &group_b);
MPI_Comm_create(MPI_COMM_WORLD, group_a, &comm_a);
MPI_Comm_create(MPI_COMM_WORLD, group_b, &comm_b);
if(comm_a != MPI_COMM_NULL)
MPI_Comm_rank(comm_a, &ma);
if(comm_b != MPI_COMM_NULL)
MPI_Comm_rank(comm_b, &mb);
if(comm_a != MPI_COMM_NULL)
lib_call(comm_a);
if(comm_b != MPI_COMM_NULL)
{
lib_call(comm_b);
lib_call(comm_b);
}
if(comm_a != MPI_COMM_NULL)
MPI_Comm_free(&comm_a);
if(comm_b != MPI_COMM_NULL)
MPI_Comm_free(&comm_b);
MPI_Group_free(&group_a);
MPI_Group_free(&group_b);
MPI_Group_free(&MPI_GROUP_WORLD);
MPI_Finalize();
}
Библиотека:
void lib_call(MPI_Comm comm)
{
int me, done = 0;
MPI_Comm_rank(comm, &me);
if(me == 0)
while(!сделано)
{
MPI_Recv(..., MPI_ANY_SOURCE, MPI_ANY_TAG, comm);
...
}
else
{
/* работа */
MPI_Send(..., 0, ARBITRARY_TAG, comm);
....
}
#ifdef EXAMPLE_2C
/* include (resp, exclude) for safety
(resp, no safety): */
MPI_Barrier(comm);
#endif
}
Приведенный выше пример - это на самом деле три примера, зависящих от того, включен или не включен процесс номер 3 в list_b, включена или не включена синхронизация в lib_call. Пример показывает, что нет необходимости защищать друг от друга последовательные обращения к lib_call с тем же самым контекстом. Безопасность будет реализована, еcли добавлена функция MPI_Barrier. Это демонстрирует тот факт, что библиотеки должны быть тщательно разработаны, даже при наличии контекстов.
Алгоритмы с недетерминированной широковещательной операцией или другие обращения с произвольными номерами поцессов-отправителей в общем случае будут уступать детерминированным реализациям ``reduce'', ``allreduce'', и ``broadcast''. Таким алгоритмам следовало бы использовать монотонно возрастающие тэги (в пределах контекста коммуникатора), чтобы сохранить корректность вычислений.
Все предшествующее формирует гипотезу ``коллективных обращений'', реализованных на основе парных обменов. Реализации MPI могут использовать для реализации коллективных операций парные обмены, хотя это и не обязательно. Парные обмены используются здесь, чтобы иллюстрировать проблемы правильности и безопасности, независимо от того, как MPI осуществляет коллективные запросы. См. также раздел 5.8.