LINUX.ORG.RU

OpenMPI: группы, коммуникаторы, широковещание

 , , , ,


0

1

Задача состоит в широковещательной передаче от чётных процессов своих номеров нечётным процессам. Как я задумал реализацию:

  • Создаю группы вида 1 чётный + все нечётные (0,1,3,5), (2,1,3,5);
  • Создаю для них коммуникаторы;
  • Использую Broadcast в коммуникаторах.

Первые два пункта сделал, вроде бы. На бродкасте застрял. Похоже ранги созданных коммуникаторов не перебираются (rank_in_comm). Да и Invalid root в MPI_Bcast.

#include "mpi.h" 
#include <stdio.h> 
#include <stdlib.h>

int main(int argc, char *argv[]) 
{ 
  int rank, rank_in_comm, size, odd_count, *ranks, msg, i, j; 
  MPI_Status status; 
  MPI_Group world_group, *groups, group;
  MPI_Comm *comms, empty_comm;

  MPI_Init(&argc, &argv); 
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  MPI_Comm_group(MPI_COMM_WORLD, &world_group);

  // Odd array for making a group
  for (i = 0; i < size; i++)
    if (i % 2 != 0) odd_count++;
  ranks = (int*)calloc(odd_count + 1, sizeof(int));
  for (i = 1, j = 0; i < size; i += 2, j++) 
  {
    ranks[i - j] = i;
//    printf("%d ", ranks[i - j]);
  }
//  printf("\n");
  
  // Allocating memory for groups & communicators
  groups = (MPI_Group*)calloc(size - odd_count, sizeof(MPI_Group));
  comms = (MPI_Comm*)calloc(size - odd_count + 1, sizeof(MPI_Comm));

  if (rank % 2 == 0)
  {
    ranks[0] = rank;
    printf("Group %d: ", rank);
    for (i = 0; i <= odd_count; i++) 
      printf("%d ", ranks[i]);
    printf("\n");
  
    MPI_Group_incl(world_group, odd_count + 1, ranks, &groups[rank]);
//    if (groups[rank]) printf("+ group\n");
    MPI_Comm_create(MPI_COMM_WORLD, groups[rank], &comms[rank]);
  }
  else MPI_Comm_create(MPI_COMM_WORLD, MPI_GROUP_EMPTY, &empty_comm);
//  if (empty_comm) printf("+ comm\n");
  if (comms[rank])
  {
    MPI_Comm_rank(comms[rank], &rank_in_comm);
    if (rank_in_comm % 2 == 0)
    {
//      printf("%d ",rank_in_comm);
//      printf("%d ",rank);
      msg = rank;
      MPI_Bcast(&msg, 1, MPI_INT, rank, comms[rank]);
      printf("%d sended its rank\n", rank); 
    } 
    else 
    {
      MPI_Bcast(&msg, 1, MPI_INT, msg, comms[rank]); 
      printf("%d received %d\n", rank, msg); 
    }
  }
  //MPI_Comm_free(comms[]);
  //MPI_Group_free(groups[]);
  free(ranks);
  MPI_Finalize(); 
  return 0; 
}

Потом переделал:

  • Создаю группу с нечётными номерами;
  • Добавляю её в коммуникатор;
  • Создаю группу для 1 чётного номера;
  • Объединяю обе группы;
  • Шлю бродкаст внутри коммуникатора.

Но и тут тоже что-то неверно. Invalid communicator в MPI_Bcast.

#include "mpi.h" 
#include <stdio.h> 
#include <stdlib.h>

int main(int argc, char *argv[]) 
{ 
  int rank, rank_in_comm, size, odd_count, *ranks, msg, i, j, single_p[1]; 
  MPI_Group world_group, group, s_group;
  MPI_Comm comm;

  MPI_Init(&argc, &argv); 
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  MPI_Comm_group(MPI_COMM_WORLD, &world_group);

  // Odd array for making a group
  for (i = 0; i < size; i++)
    if (i % 2 != 0) odd_count++;
  ranks = (int*)calloc(odd_count, sizeof(int));
  for (i = 1, j = 1; i < size; i += 2, j++) 
  {
    ranks[i - j] = i;
//    printf("%d ", ranks[i - j]);
  }
//  printf("\n");
  

  printf("Process %d odd group: ", rank);
  for (i = 0; i < odd_count; i++) 
    printf("%d ", ranks[i]);
  printf("\n");

  MPI_Group_incl(world_group, odd_count, ranks, &group);
  MPI_Comm_create(MPI_COMM_WORLD, group, &comm);
  if (rank % 2 == 0)
  {
    single_p[1] = rank;
    MPI_Group_incl(world_group, 1, &single_p[1], &s_group);
    MPI_Group_union(s_group, group, &group);
    MPI_Group_size(group, &size);
    printf("Group size: %d\n", size);
    MPI_Group_rank(group, &rank_in_comm);
    int root = rank; printf("Root: %d, Rank in comm: %d\n", root, rank_in_comm);
      if (rank_in_comm % 2 == 0)
      {
        msg = rank;
        MPI_Bcast(&msg, 1, MPI_INT, root, comm);
        printf("%d sended its rank\n", rank_in_comm);
      }
      else
      {
        MPI_Bcast(&msg, 1, MPI_INT, root, comm);
        printf("%d received %d\n", rank_in_comm, msg);
      }
//    MPI_Group_excl(group, 1, &single_p[1], &group);
//    MPI_Comm_free(&comm);
//    MPI_Group_free(&group);
  }
  free(ranks);
  MPI_Finalize(); 
  return 0; 
}

Кто-нибудь может помочь разобраться?


Задача состоит в широковещательной передаче от чётных процессов своих номеров нечётным процессам.

Учебная что ли? А что, простые смертные unicast передачи 0->1, 0->3, 0->5, 2->1, 2->3, 2->5, 4->1, 4->3, 4->5 по MPI_COMM_WORLD препод обещал не засчитывать?

t184256 ★★★★★
()
  1. У тебя две группы процессов между которыми надо общаться => Нужен интеркоммуникатор. Смотрим MPI_Comm_split, MPI_Intercomm_create
  2. Тебе надо из одинаковых элементов, собрать один массив на нескольких узлах. Смотрим MPI_Allgather.

Как следствие твой код будет содержать пару команд на разбиение Word, с последующим построением интеркоммуникатора. И ОДНА команда пересылки/сборки данных.

З.Ы. Когда будешь писать MPI_Allgather над созданным интеркоммуникатором, то: «Каждый чётный элемент посылают один int и ожидают 0 int-ов от каждого нечётного. Каждый нечётный посылает 0 int-ов и ожидает по 1 int от каждого чётного»

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Учебная что ли?

Да.

MPI_Allgather

В формулировке задания указана MPI_Bcast. Или таки с ней такое не реализовать?

t0n
() автор топика
Ответ на: комментарий от t0n

В формулировке задания указана MPI_Bcast.

Раннее ты говорил:

Задача состоит в широковещательной передаче от чётных процессов своих номеров нечётным процессам.

  • MPI_Allgather - это одна из функций широковещательной передачи данных.
  • MPI_Bcast - для передачи от одного ко многим. MPI_Allgather - от многих ко многим.
  • Твоя задача предполагает от многих ко многим.

Так что использование MPI_Bcast в этой задаче такая же глупость, как и

0->1, 0->3, 0->5, 2->1, 2->3, 2->5, 4->1, 4->3, 4->5

З.Ы.: Ещё раз, твоя задача решается одной командой для пересылки и двумя командами для создания интеркоммуникатора, приводить пример кода не считаю пока уместным, т.к. это «учебная задача».

AlexVR ★★★★★
()
Ответ на: комментарий от t0n

Если нет понимания как работают MPI_Comm_split, MPI_Intercomm_create и MPI_Allgather, то вначале посмотри примеры их использования, и только потом можешь задавать вопросы. Если что комментарии дам. А из-за вредности сам код показывать не буду. Очень надеюсь, что ты его сам напишешь и тут опубликуешь.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Продолжаю биться.

#include "mpi.h" 
#include <stdio.h> 
#include <stdlib.h>

int main(int argc, char *argv[]) 
{ 
  int rank, msg; 
  MPI_Comm comm, intercomm;

  MPI_Init(&argc, &argv); 
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_split(MPI_COMM_WORLD, rank % 2, rank, &comm);
  if (rank % 2 == 0)
    MPI_Intercomm_create(comm, 0, MPI_COMM_WORLD, 1, 0, &intercomm);
  else
    MPI_Intercomm_create(comm, 0, MPI_COMM_WORLD, 0, 0, &intercomm);

  if (rank % 2 == 0)
  {
    msg = rank;
    MPI_Allgather(&msg, 1, MPI_INT, NULL, 0, MPI_INT, comm);
    printf("%d sended its rank\n", rank);
  }
  else
  {
    MPI_Allgather(NULL, 0, MPI_INT, &msg, 1, MPI_INT, comm);
    printf("%d received %d\n", rank, msg);
  }

  MPI_Comm_free(&intercomm);
  MPI_Comm_free(&comm);
  MPI_Finalize(); 
  return 0; 
}

t0n
() автор топика
Ответ на: комментарий от t0n

Так Allgather хотя бы верные нечётные ранги выводит. Но надо всё же intercomm вместо comm. Ну да всё равно не тот результат.

t0n
() автор топика
Ответ на: комментарий от t0n

Но надо всё же intercomm вместо comm

Для того его и создавали.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Сделал массив, соответственно изменил sendcount/recvcount по числу элементов в нём.

#include "mpi.h" 
#include <stdio.h> 
#include <stdlib.h>

int main(int argc, char *argv[]) 
{ 
  int size, rank, *msg, i, even_count;
  MPI_Comm worldcommdup, comm, intercomm;

  MPI_Init(&argc, &argv);
  MPI_Comm_dup(MPI_COMM_WORLD, &worldcommdup);
  MPI_Comm_size(worldcommdup, &size);
  for (i = 0; i < size; i++)
    if (i % 2 == 0) even_count++;
  msg = (int*)calloc(even_count, sizeof(int));

  MPI_Comm_rank(worldcommdup, &rank);
  MPI_Comm_split(worldcommdup, rank % 2, rank, &comm);
  if (rank % 2 == 0)
    MPI_Intercomm_create(comm, 0, worldcommdup, 1, 0, &intercomm);
  else
    MPI_Intercomm_create(comm, 0, worldcommdup, 0, 0, &intercomm);

  if (rank % 2 == 0)
  {
    i = 0; if (rank != 0) while (rank - i != rank / 2) i++;
    msg[rank - i] = rank;
    MPI_Allgather(&msg, even_count, MPI_INT, NULL, 0, MPI_INT, intercomm);
    printf("%d sended its rank\n", rank);
  }
  else
  {
    MPI_Allgather(NULL, 0, MPI_INT, &msg, even_count, MPI_INT, intercomm);
    for (i = 0; i < even_count; i++)
      printf("%d received %d\n", rank, msg[i]);
  }

  MPI_Comm_free(&intercomm);
  MPI_Comm_free(&comm);
  MPI_Comm_free(&worldcommdup);
  MPI_Finalize(); 
  return 0; 
}
Вывод:
mpirun -n 6 lab4.5
4 sended its rank
2 sended its rank
0 sended its rank
[pel1:10796] *** Process received signal ***
[pel1:10796] Signal: Segmentation fault (11)
[pel1:10796] Signal code: Address not mapped (1)
[pel1:10796] Failing at address: 0x7f7f00000004
[pel1:10794] *** Process received signal ***
[pel1:10794] Signal: Segmentation fault (11)
[pel1:10794] Signal code: Address not mapped (1)
[pel1:10794] Failing at address: 0x7fd100000004

t0n
() автор топика
Ответ на: комментарий от t0n

Но надо всё же intercomm вместо comm.

Я тоже упражнялся с Allgather. Оказывается, уже несколько лет назад предложили вариант погибче, но новый стандарт MPI это проигнорировал и привнёс только косметические исправления, хотя версия была мажорная. Так что остаётся только мучаться. Но вот интеркомы я не использовал, а подбирал индексы для Allgather. Хотя, может, с интеркомом, было бы и проще. Надо будет учесть.

[pel1:10796] *** Process received signal ***

Отлаживать Allgather - одно умиление. Чуть что не так - бабах. А что? В обломках бегло ничего не различить.

gag ★★★★★
()
Ответ на: комментарий от t0n

MPI_Allgather(&msg, even_count, MPI_INT, NULL, 0, MPI_INT, intercomm);

Отправляем одно число (ранг), значит надо:

MPI_Allgather(&rank, 1, MPI_INT, NULL, 0, MPI_INT, intercomm);

Аналогично, получаем по одному числу в заранее выделенный массив:

MPI_Allgather(NULL, 0, MPI_INT, msg, 1, MPI_INT, intercomm);
AlexVR ★★★★★
()
Ответ на: комментарий от t0n

Ну тогда мой код :)

#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  int i;

  int world_size;
  int world_rank;
  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
  MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

  int is_odd = world_rank & 1;
  int evens_count = world_size >> 1;

  MPI_Comm my_parity_sub_comm; ///< Коммуникатор подоблости (чётные/нечётные)
  MPI_Comm evens_odds_inercomm; ///< Интеркоммуникатор для общения чётных и нечётных узлов

  MPI_Comm_split(MPI_COMM_WORLD, is_odd, world_rank, &my_parity_sub_comm);
  MPI_Intercomm_create(my_parity_sub_comm, 0, MPI_COMM_WORLD, 1 - is_odd, 1, &evens_odds_inercomm);

  int *data = is_odd ? (int *)calloc(evens_count, sizeof(int)) : NULL;

  MPI_Allgather(&world_rank, 1 - is_odd, MPI_INT, data, is_odd, MPI_INT, evens_odds_inercomm);

  printf("%d: ", world_rank);
  if (data) {
    for (i = 0; i < evens_count; ++i) {
      printf("%d,", data[i]);
    }
    printf("\n");
  } else {
    printf("NULL\n");
  }

  if (data)
    free(data);

  MPI_Finalize();
  return 0;
}
AlexVR ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.