MPI Broadcast 2D array - PullRequest
       73

MPI Broadcast 2D array

3 голосов
/ 01 декабря 2011

У меня есть двумерный массив двойной точности, который обрабатывается параллельно несколькими процессами. Каждый процесс манипулирует частью массива, и в конце каждой итерации мне нужно убедиться, что все процессы имеют одну и ту же копию 2D-массива.

Предполагается массив размером 10 * 10 и 2 процесса (или процессора). Процесс 1 (P1) манипулирует первыми 5 строками 2D-строки (всего 5 * 10 = 50 элементов), а P2 манипулирует последними 5 строками (всего 50 элементов). И в конце каждой итерации мне нужно иметь P1 (ITS OWN первые 5 строк + последние 5 строк P2). P2 должен иметь (первые 5 строк P1 + последние 5 строк OWN). Надеюсь, сценарий ясен.

Я пытаюсь вещать, используя код, указанный ниже. Но моя программа продолжает выходить с этой ошибкой: «ПРИЛОЖЕНИЕ ПРЕКРАЩЕНО СТРОКОЙ ВЫХОДА: Зависание (сигнал 1)».

Я уже использую непрерывный распределитель памяти 2D, как указано здесь: MPI_Bcast динамический 2d массив от Jonathan. Но я все еще получаю ту же ошибку.

Может ли кто-нибудь мне помочь?

Мой код:

double **grid, **oldgrid;
int gridsize; // size of grid
int rank, size; // rank of current process and no. of processes
int rowsforeachprocess, offset; // to keep track of rows that need to be handled by each process

 /* allocation, MPI_Init, and lots of other stuff */

 rowsforeachprocess = ceil((float)gridsize/size);
 offset = rank*rowsforeachprocess;

 /* Each process is handling "rowsforeachprocess" #rows.
 * Lots of work done here
 * Now I need to broadcast these rows to all other processes.
 */

 for(i=0; i<gridsize; i++){
     MPI_Bcast(&(oldgrid[i]), gridsize-2, MPI_DOUBLE, (i/rowsforeachprocess), MPI_COMM_WORLD);
 }

Часть 2. Приведенный выше код является частью параллельного решателя уравнения Лапласа с использованием одномерного разложения, и я не хотел использовать модель Мастер-работник. Будет ли мой код проще, если я использую модель Мастер-работник?

1 Ответ

2 голосов
/ 02 декабря 2011

Проблема, вызывающая сбой, - это проблема с указателем в 2d-массиве - & (oldgrid [i]) - это указатель на указатель на double, а не указатель на double, и он указывает на указатель на строка i вашего массива, а не строка i массива. Вы хотите MPI_Bcast(&(oldgrid[i][0]),.. или MPI_Bcast(oldgrid[i],....

Есть и другой способ сделать это, который использует только один дорогой коллективный коммуникатор вместо одного в строке; если вам нужно, чтобы у всех была копия всего массива, вы можете использовать MPI_Allgather для сбора данных и их распространения среди всех; или, в общем случае, когда процессы не имеют одинаковое количество строк, MPI_Allgatherv . Вместо цикла по трансляциям это будет выглядеть примерно так:

{
    int *counts = malloc(size*sizeof(int));
    int *displs = malloc(size*sizeof(int));
    for (int i=0; i<size; i++) {
        counts[i] = rowsforeachprocess*gridsize;
        displs[i] = i*rowsforeachprocess*gridsize;
    }
    counts[size-1] = (gridsize-(size-1)*rowsforeachprocess)*gridsize;

    MPI_Allgatherv(oldgrid[offset], mynumrows*gridsize, MPI_DOUBLE,
                   oldgrid[0],      counts, displs, MPI_DOUBLE, MPI_COMM_WORLD);
    free(counts);
    free(displs);
}

где count - это количество элементов, отправленных каждой задачей, а displs - смещения.

Но, наконец, вы уверены, что у каждого процесса должна быть копия всего массива? Если вы просто вычисляете лапласиан, вам, вероятно, просто нужны соседние строки, а не весь массив.

Это будет выглядеть так:

int main(int argc, char**argv) {
double **oldgrid;
const int gridsize=10; // size of grid
int rank, size;        // rank of current process and no. of processes
int rowsforeachprocess; // to keep track of rows that need to be handled by each process
int offset, mynumrows;
MPI_Status status;

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

rowsforeachprocess = (int)ceil((float)gridsize/size);
offset = rank*rowsforeachprocess;
mynumrows = rowsforeachprocess;
if (rank == size-1)
    mynumrows = gridsize-offset;

rowsforeachprocess = (int)ceil((float)gridsize/size);
offset = rank*rowsforeachprocess;
mynumrows = rowsforeachprocess;
if (rank == size-1)
    mynumrows = gridsize-offset;

malloc2ddouble(&oldgrid, mynumrows+2, gridsize);

for (int i=0; i<mynumrows+2; i++)
    for (int j=0; j<gridsize; j++)
        oldgrid[i][j] = rank;

/* exchange row data with neighbours */
int highneigh = rank+1;
if (rank == size-1) highneigh = 0;

int lowneigh  = rank-1;
if (rank == 0)  lowneigh = size-1;

/* send data to high neibhour and receive from low */

MPI_Sendrecv(oldgrid[mynumrows], gridsize, MPI_DOUBLE, highneigh, 1,
             oldgrid[0],         gridsize, MPI_DOUBLE, lowneigh,  1,
             MPI_COMM_WORLD, &status);

/* send data to low neibhour and receive from high */

MPI_Sendrecv(oldgrid[1],           gridsize, MPI_DOUBLE, lowneigh,  1,
             oldgrid[mynumrows+1], gridsize, MPI_DOUBLE, highneigh, 1,
             MPI_COMM_WORLD, &status);


for (int proc=0; proc<size; proc++) {
    if (rank == proc) {
        printf("Rank %d:\n", proc);
        for (int i=0; i<mynumrows+2; i++) {
            for (int j=0; j<gridsize; j++) {
                printf("%f ", oldgrid[i][j]);
            }
            printf("\n");
        }
        printf("\n");
    }
    MPI_Barrier(MPI_COMM_WORLD);
}
...