Как получить столбцы MPI_Gatherv от процессора, где каждый процесс может отправлять различное количество столбцов - PullRequest
6 голосов
/ 21 марта 2011

Скажем, участвуют 2 процесса. Процесс 0 (ранг 0) имеет

A = { a d
      b e
      c f
    }

и процесс 1 (ранг 1) имеет

A = { g
      h
      i
    }

Я хочу, чтобы оба процессора отправили эти столбцы на ранг 0, чтобы ранг 0 имел следующее, скажем, в другом 2D-массиве.

B = { a d g
      b e h
      c f i
    }

Я создаю новый тип данных столбца для MPI_Gatherv и пробую следующий код, который не дает мне понять, где.

Мои конкретные вопросы:

  1. Как мне подойти к этому
  2. Какими должны быть send_type и recv_type.
  3. Как указывать смещения (если они относятся к новому типу данных или MPI_CHAR)

Спасибо.

Это мой код:

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

int main(int argc, char *argv[])
{
  int numprocs, my_rank;
   long int i, j;
   MPI_Status status;
   char **A;
   char **B;
  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
  MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

  if(my_rank == 0)
  {
    A = (char **)calloc((3), sizeof(char *));
    B = (char **)calloc((3), sizeof(char *));
    for(i=0; i<3; ++i)
    {
       A[i] = (char *)calloc(2, sizeof(char));
       B[i] = (char *)calloc(3, sizeof(char));
    }

    A[0][0] = 'a';
    A[1][0] = 'b';
    A[2][0] = 'c';
    A[0][1] = 'd';
    A[1][1] = 'e';
    A[2][1] = 'f';
  }
  else
  {
    A = (char **)calloc((3), sizeof(char *));
    for(i=0; i<3; ++i)
    {
       A[i] = (char *)calloc(1, sizeof(char));
    }
    A[0][0] = 'g';
    A[1][0] = 'h';
    A[2][0] = 'i';

  }
  MPI_Datatype b_col_type;
  MPI_Type_vector(3, 1, 1, MPI_CHAR, &b_col_type);
  MPI_Type_commit(&b_col_type);
  int displs[2] = {0, 2};
  int recvcounts[2] = {2, 1};
  MPI_Gatherv(&A[0][0], recvcounts[my_rank], b_col_type, &B[0][0], recvcounts, displs,    b_col_type, 0, MPI_COMM_WORLD);
  if(my_rank == 0)
  {
    for(i=0; i<3; ++i)
    {
      for(j=0; j<3; ++j)
        printf("%c ", B[i][j]);
      printf("\n");
    }
  }
  MPI_Finalize();
  return 0;
}

1 Ответ

6 голосов
/ 21 марта 2011

Итак, во-первых - и это постоянно встречается с массивами MPI и C - вы не можете сделать стандартную двумерную матрицу C.Давайте посмотрим на это:

A = (char **)calloc((3), sizeof(char *));
for(i=0; i<3; ++i)
{
   A[i] = (char *)calloc(2, sizeof(char));
}

Это определенно выделит массив символов 3x2, но вы не представляете, как результирующие данные располагаются в памяти.В частности, на all нет гарантии, что A[1][0] сразу же следует за A[0][1].Это делает очень трудным создание типов данных MPI, которые охватывают структуру данных!Вам нужно выделить 3x2 смежных байта, а затем указать в нем массив:

char **charalloc2d(int n, int m) {
    char *data = (char *)calloc(n*m,sizeof(char));
    char **array = (char **)calloc(n, sizeof(char *));
    for (int i=0; i<n; i++)
        array[i] = &(data[i*m]);

    return array;
}

void charfree2d(char **array) {
    free(array[0]);
    free(array);
    return;
}

/* ... */
nrows = 3;
ncols = 2;
A = charalloc2d(nrows,ncols);

Теперь мы знаем кое-что о компоновке массива и можем зависеть от этого при создании типов данных.

Вы на правильном пути с типами данных -

MPI_Datatype b_col_type;
MPI_Type_vector(3, 1, 1, MPI_CHAR, &b_col_type);
MPI_Type_commit(&b_col_type);

сигнатура MPI_Type_vector: (count, blocklen, stepde, old_type, * newtype).
Нам нужны символы nrows,которые входят в блоки 1;но они разнесены друг от друга;так что это шаг.

Обратите внимание, что это действительно тип столбца массива A, а не B;тип будет зависеть от количества столбцов в массиве.Таким образом, каждый процесс использует свой тип отправки, и это нормально.

MPI_Datatype a_col_type;
MPI_Type_vector(nrows, 1, ncols, MPI_CHAR, &a_col_type);
MPI_Type_commit(&a_col_type);

Последний шаг - MPI_Gatherv, и здесь вам нужно быть немного симпатичным.Хитрость в том, что мы хотим посылать (и получать) несколько таких вещей одновременно, то есть несколько последовательных.Но нам нужно, чтобы следующий столбец был не только символом, а всего лишь одним символом.К счастью, мы можем сделать это, установив верхнюю границу структуры данных на расстоянии всего одного символа от нижней границы, чтобы следующий элемент начинался в правильном месте.Это допускается стандартом , и фактически один из их примеров в разделе 4.1.4 основан на этом.

Чтобы сделать это, мы создаем тип с измененным размером, заканчивающийся одним байтомпосле запуска:

MPI_Type_create_resized(a_col_type, 0, 1*sizeof(char), &new_a_col_type);
MPI_Type_commit(&new_a_col_type); 

и аналогично для B;и теперь мы можем отправлять и получать кратные из них, как и следовало ожидать.Так что у меня работает следующее:

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

char **charalloc2d(int n, int m) {
    char *data = (char *)calloc(n*m,sizeof(char));
    char **array = (char **)calloc(n, sizeof(char *));
    for (int i=0; i<n; i++)
        array[i] = &(data[i*m]);

    return array;
}

void charfree2d(char **array) {
    free(array[0]);
    free(array);
    return;
}


int main(int argc, char *argv[])
{
    int numprocs, my_rank;
    int nrows, ncols, totncols;
    long int i, j;
    char **A;
    char **B;
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    if(my_rank == 0)
    {
        nrows=3;
        ncols=2;
        totncols = 3;

        A = charalloc2d(nrows, ncols);
        B = charalloc2d(nrows, totncols);

        A[0][0] = 'a';
        A[1][0] = 'b';
        A[2][0] = 'c';
        A[0][1] = 'd';
        A[1][1] = 'e';
        A[2][1] = 'f';
    }
    else
    {
        nrows = 3;
        ncols = 1;
        A = charalloc2d(nrows, ncols);
        B = charalloc2d(1,1); /* just so gatherv survives */
        A[0][0] = 'g';
        A[1][0] = 'h';
        A[2][0] = 'i';

    }
    MPI_Datatype a_col_type, new_a_col_type;
    MPI_Type_vector(nrows, 1, ncols, MPI_CHAR, &a_col_type);
    MPI_Type_commit(&a_col_type);

    /* make the type have extent 1 character -- now the next
     * column starts in the next character of the array 
     */
    MPI_Type_create_resized(a_col_type, 0, 1*sizeof(char), &new_a_col_type);
    MPI_Type_commit(&new_a_col_type);

    MPI_Datatype b_col_type, new_b_col_type;
    if (my_rank == 0) {
        MPI_Type_vector(nrows, 1, totncols, MPI_CHAR, &b_col_type);
        MPI_Type_commit(&b_col_type);

        /* similarly "resize" b columns */
        MPI_Type_create_resized(b_col_type, 0, 1*sizeof(char), &new_b_col_type);
        MPI_Type_commit(&new_b_col_type);
    }

    int displs[2] = {0, 2};
    int recvcounts[2] = {2, 1};
    MPI_Gatherv(A[0], recvcounts[my_rank], new_a_col_type,
                B[0], recvcounts, displs, new_b_col_type,
                0, MPI_COMM_WORLD);
    if(my_rank == 0)
    {
        for(i=0; i<3; ++i)
        {
            for(j=0; j<3; ++j)
                printf("%c ", B[i][j]);
            printf("\n");
        }
    }
    MPI_Finalize();
    return 0;
}
...