Итак, во-первых - и это постоянно возникает с массивами MPI и C - вы не можете делать стандартную вещь двумерного массива C. Давайте посмотрим на это:
A = (char **)calloc((3), sizeof(char *));
for(i=0; i<3; ++i)
{
A[i] = (char *)calloc(2, sizeof(char));
}
Это определенно выделит массив символов 3x2, но вы понятия не имеете, как результирующие данные размещаются в памяти. В частности, нет никакой гарантии вообще, что 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, stride, old_type, *newtype).
Нам нужно nrows символов, которые идут блоками по 1; но они отстоят друг от друга на ncol; так что это шаг.
Обратите внимание, что на самом деле это тип столбца массива 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
, и здесь вам нужно быть немного милым. Хитрость в том, что мы хотим отправлять (и получать) несколько таких вещей одновременно, то есть несколько последовательных. Но нам нужно, чтобы следующий столбец находился не на расстоянии nrows*ncols символов, а всего на один символ. К счастью, мы можем сделать это, установив верхнюю границу структуры данных на расстоянии всего одного символа от нижней границы, чтобы следующий элемент начинался в нужном месте. Это разрешено стандартом, и фактически одним их примеры в разделе 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;
}
person
Jonathan Dursi
schedule
21.03.2011