Столбец MPI циклическое распределение 2d-массива от корня к другим процессам

#c #multithreading #mpi

#c #многопоточность #mpi

Вопрос:

У меня есть программа на C, которая использует библиотеку MPI. Я инициализировал динамический двумерный массив, измерения которого (строки и столбцы) считываются из stdin корня процесса.

Когда я пытаюсь циклически распределить элементы (столбцы) между другими процессами, я не добиваюсь никакого прогресса. Я использую MPI_Scatter для распределения столбцов по другим процессам в массиве. Здесь я использую производный тип данных MPI_Type_vector для двумерного массива.

Конечно, он распределяет только первый столбец по локальным одномерным массивам процессов. Что касается остального, я ввел MPI_Scatter цикл for, и теперь у меня распределены все столбцы, но только для случая, когда количество процессов и размеры матрицы равны. Как я мог бы распределить более одного столбца по процессу с помощью MPI_Scatter ?

На данный момент я сомневаюсь, что это лучшая попытка решить проблему, потому что должен быть лучший способ с меньшим количеством сообщений.

Разумнее ли использовать одномерный массив для матрицы вместо двумерного массива?

Редактировать:

После небольшого размышления становится очевидным, что если я использую цикл for, производный тип данных MPI_Type_vector становится ненужным. Это указывает на то, что цикл for не продвигает меня дальше.

 for(i=0 ;i<m; i  )
    MPI_Scatter(amp;(array[i][0]), 1,  ub_mpi_t, amp;local_array[i], 1, MPI_DOUBLE, 0,
                 MPI_COMM_WORLD) ;
  

Ответ №1:

Хорошо, итак, давайте сначала попробуем простой случай — на каждый процесс приходится ровно один столбец. Ниже приведена моя слегка отредактированная версия того, что у вас есть выше; различия, на которые я хочу обратить внимание, заключаются лишь в том, что мы изменили способ распределения массива A, и мы просто используем тип данных one vector:

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

int main(int argc, char** argv)
{
  double **A = NULL ;   /*2D array initialised on process 0 */
  double *Adata = NULL;
  double *sendbufptr = NULL;
  int i,j ;
  double *column ; /*1D array for column */
  const int columnlen=6;
  int my_rank, p ;
  MPI_Datatype vector_mpi_t ;

  MPI_Init(amp;argc,amp;argv) ;

  MPI_Comm_rank(MPI_COMM_WORLD,amp;my_rank) ;
  MPI_Comm_size(MPI_COMM_WORLD,amp;p) ;

  /*initialise 2D array on process 0 and allocate memory*/
  if(my_rank==0)
  {
    A = (double**)malloc(p*sizeof(double *)) ;
    Adata = (double *)malloc(p*columnlen*sizeof(double));
    for(i=0;i<p;i  )
      A[i] = amp;(Adata[i*columnlen]);

    for (i=0; i<p; i  ) 
        for (j=0; j<columnlen; j  )
            A[i][j] = i;

    /* print 2D array to screen */
    printf("Rank 0's 2D array:n");
    for(i=0;i<p;i  )
    {
      for(j=0;j<columnlen;j  )
        printf( "%lf " , A[i][j]) ;
      printf( "n") ;
    }
    printf( "n") ;
    printf( "n") ;
  }
  /* initialise and allocate memory for 1d column array on every process */
  column = (double*)malloc(columnlen*sizeof(double)) ;
  for(i=0;i<columnlen;i  )
  {
    column[i] = 0 ;
  }

  /*derived datatype for 2D array columns*/
  MPI_Type_vector(columnlen,1,1,MPI_DOUBLE,amp;vector_mpi_t) ;
  MPI_Type_commit(amp;vector_mpi_t);

  sendbufptr = NULL;
  if (my_rank == 0) sendbufptr=amp;(A[0][0]);
  MPI_Scatter(sendbufptr, 1, vector_mpi_t, column, 1, vector_mpi_t, 0, MPI_COMM_WORLD);
  /*print column on every process */

   printf("Rank %d's column: n", my_rank);
   for(i=0;i<columnlen;i  )
   {
      printf( "%lf " , column[i]) ;
   }
   printf( "n") ;


  MPI_Finalize() ;

  free(column);
  free(Adata);
  free(A);

  return 0;
}
  

Ключевым моментом здесь является то, что MPI_Scatter принимает указатель на блок данных, а не указатели на указатели. Таким образом, он не будет разыменовывать A[1] и затем отправлять то, что указывает туда, а затем A [2] и то, что указывает туда, и т.д. Ожидается непрерывный блок данных. Итак, мы определили, как данные A размещаются в памяти (обратите внимание, что обычно это правильный способ делать что-либо в любом случае для численных вычислений) — за столбцом данных следует следующий столбец данных и т.д. (Хотя то, как я распечатываю данные, больше похоже на строки, но неважно.)

Также обратите внимание, что в вызове MPI_Scatter я не могу просто использовать amp;(A[0][0]), потому что это разыменование нулевого указателя во всех процессах, кроме одного.

Переход от одного столбца к нескольким довольно прост; структура данных столбца переходит от одномерного массива к двумерному массиву, расположенному как is .

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

int main(int argc, char** argv)
{ 
  double **A = NULL ;   /*2D array initialised on process 0 */
  double *Adata = NULL;
  double *sendbufptr = NULL;
  int i,j ;
  double **columns ; /*2D array for column */
  double *columndata;
  const int columnlen=6;
  int ncolumns;
  int my_rank, p ;
  MPI_Datatype vector_mpi_t ;

  MPI_Init(amp;argc,amp;argv) ;

  MPI_Comm_rank(MPI_COMM_WORLD,amp;my_rank) ;
  MPI_Comm_size(MPI_COMM_WORLD,amp;p) ;

  ncolumns = 2*p;

  /*initialise 2D array on process 0 and allocate memory*/
  if(my_rank==0)
  {
    A = (double**)malloc(ncolumns*sizeof(double *)) ;
    Adata = (double *)malloc(ncolumns*columnlen*sizeof(double));
    for(i=0;i<ncolumns;i  )
      A[i] = amp;(Adata[i*columnlen]);

    for (i=0; i<ncolumns; i  ) 
        for (j=0; j<columnlen; j  )
            A[i][j] = i;

    /* print 2D array to screen */
    printf("Rank 0's 2D array:n");
    for(i=0;i<ncolumns;i  )
    {
      for(j=0;j<columnlen;j  )
        printf( "%lf " , A[i][j]) ;
      printf( "n") ;
    }
    printf( "n") ;
    printf( "n") ;
  }
  /* initialise and allocate memory for 1d column array on every process */
  columndata = (double*)malloc((ncolumns/p)*columnlen*sizeof(double)) ;
  columns = (double **)malloc((ncolumns/p)*sizeof(double *));
  for(i=0;i<(ncolumns/p);i  )
  {
    columns[i] = amp;(columndata[i*columnlen]);
  }

  /*derived datatype for 2D array columns*/
  MPI_Type_vector(columnlen,1,1,MPI_DOUBLE,amp;vector_mpi_t) ;
  MPI_Type_commit(amp;vector_mpi_t);

  sendbufptr = NULL;
  if (my_rank == 0) sendbufptr=amp;(A[0][0]);
  MPI_Scatter(sendbufptr, (ncolumns/p), vector_mpi_t, amp;(columns[0][0]), (ncolumns/p), vector_mpi_t, 0, MPI_COMM_WORLD);

  /*print columns on every process */

   printf("Rank %d's columns: n", my_rank);
   for(i=0;i<ncolumns/p;i  )
   {
     printf( "[%d]: ", my_rank) ;
     for(j=0;j<columnlen;j  )
     {
        printf( "%lf " , columns[i][j]) ;
     }
     printf( "n") ;
  }

  MPI_Finalize() ;

  free(columns);
  free(Adata);
  free(A);

  return 0;
}
  

И затем переход к различному количеству столбцов на процессор требует использования MPI_Scatterv, а не MPI_Scatter.

Ответ №2:

Я не уверен, что полностью понимаю, что вы пытаетесь сделать, и как, так что это может быть не так:

Похоже, что вы пытаетесь распределить столбцы 2D-массива по нескольким процессам. Возможно, у вас есть массив из 10 столбцов и 4 процесса, поэтому 2 процесса получат по 3 столбца каждый, а 2 процесса — по 2 столбца каждый? Конечно, процесс 0 «получает» 3 столбца от самого себя.

Ваш первый шаг, который вы предприняли, — это определить MPI_Type_vector, который определяет столбец массива.

Далее, и здесь я немного озадачен вашим использованием MPI_Scatter, почему бы просто не написать цикл, который отправляет столбцы 1,2,3,4,5,6,7,8,9,10 процессам 0,1,2,3,0,1,2,3,0,1 (о, предварительно договорившись, что у принимающих процессов есть двумерный массив, в который можно помещать столбцы по мере их поступления)?

Проблема с использованием MPI_Scatter для этого заключается в том, что вам приходится (я думаю) отправлять одинаковый объем данных каждому из принимающих процессов, что, как вы заметили, не сработает, если количество процессов точно не делится на количество столбцов в вашем массиве. Если бы вам пришлось использовать MPI_Scatter, то вам, возможно, пришлось бы дополнить ваш массив дополнительными столбцами, но это кажется немного бессмысленным.

Наконец, вы могли бы сделать то, что хотите, в инструкции 1 с помощью MPI_Type_create_subarray или MPI_Type_create_darray, но у меня нет опыта их использования.