Итак, во-первых - и это постоянно встречается с массивами 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;
}