#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, но у меня нет опыта их использования.