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 ★★★★★ ()
Ответ на: комментарий от 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 ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.