J’ai un problème étrange lié à une structure C qui est communiquée à l’aide d’un type de données dérivé de MPI. L’exemple ci-dessous fonctionne; il envoie simplement un message composé d’un integer
plus 4 valeurs float
.
Exemple de travail Minmum:
#include #include int main(int argc, char *argv[]) { MPI_Init(&argc, &argv); int i, rank, tag = 1; MPI_Status status; MPI_Comm_rank(MPI_COMM_WORLD, &rank); // Array of doubles plus element count typedef struct { int row; float elements[4]; } My_array; // Derived datatype for an array of doubles plus element count MPI_Datatype MY_ARRAY_TYPE; const int nr_blocks = 2; int blocklengths[2] = {1, 4}; MPI_Datatype oldtypes[2] = {MPI_INT, MPI_FLOAT}; MPI_Aint extent, lb; MPI_Type_get_extent(MPI_INT, &lb, &extent); MPI_Aint displacements[2] = {0, extent}; MPI_Type_create_struct(nr_blocks, blocklengths, displacements, oldtypes, &MY_ARRAY_TYPE); MPI_Type_commit(&MY_ARRAY_TYPE); if(rank == 0) { My_array array1 = {3, 3.1, 3.2, 3.3, 3.4}; MPI_Send(&array1, 1, MY_ARRAY_TYPE, 1, tag, MPI_COMM_WORLD); } if(rank == 1) { My_array array2; MPI_Recv(&array2, 1, MY_ARRAY_TYPE, 0, tag, MPI_COMM_WORLD, &status); printf("Rank %d received elements of row %d:\n", rank, array2.row); for(i = 0; i < 4; i++) printf("\t%.1f\n", array2.elements[i]); } MPI_Type_free(&MY_ARRAY_TYPE); MPI_Finalize(); }
Si vous avez access à une installation MPI, l’exemple peut être compilé par mpicc -o example example.c
et exécuté par l’ mpirun -np 2 example
. La sortie devrait être
Rank 1 received elements of row 3: 3.1 3.2 3.3 3.4
Le problème: à présent, lorsque le tableau de float
s est remplacé par un tableau de double
s et que MPI_FLOAT
alors MPI_DOUBLE
, le résultat obtenu est erroné.
Ce code:
#include #include int main(int argc, char *argv[]) { MPI_Init(&argc, &argv); int i, rank, tag = 1; MPI_Status status; MPI_Comm_rank(MPI_COMM_WORLD, &rank); // Array of doubles plus element count typedef struct { int row; double elements[4]; } My_array; // Derived datatype for an array of doubles plus element count MPI_Datatype MY_ARRAY_TYPE; const int nr_blocks = 2; int blocklengths[2] = {1, 4}; MPI_Datatype oldtypes[2] = {MPI_INT, MPI_DOUBLE}; MPI_Aint extent, lb; MPI_Type_get_extent(MPI_INT, &lb, &extent); MPI_Aint displacements[2] = {0, extent}; MPI_Type_create_struct(nr_blocks, blocklengths, displacements, oldtypes, &MY_ARRAY_TYPE); MPI_Type_commit(&MY_ARRAY_TYPE); if(rank == 0) { My_array array1 = {3, 3.1, 3.2, 3.3, 3.4}; MPI_Send(&array1, 1, MY_ARRAY_TYPE, 1, tag, MPI_COMM_WORLD); } if(rank == 1) { My_array array2; MPI_Recv(&array2, 1, MY_ARRAY_TYPE, 0, tag, MPI_COMM_WORLD, &status); printf("Rank %d received elements of row %d:\n", rank, array2.row); for(i = 0; i < 4; i++) printf("\t%.1f\n", array2.elements[i]); } MPI_Type_free(&MY_ARRAY_TYPE); MPI_Finalize(); }
produit:
Rank 1 received elements of row 3: 3.1 3.2 3.3 0.0
J’ai essayé un peu, en utilisant d’autres données dans la structure et le type de données dérivé (par exemple, un tableau d’entiers au lieu d’un, int
/ MPI_INT
au lieu de float
/ MPI_FLOAT
, etc.) et j’ai constaté que le problème ne se MPI_FLOAT
que lorsque les doublons utilisé. Ce qui me laisse penser que cela pourrait être un problème d’alignement – mais je suis coincé là. MPI devrait s’occuper des alignements automatiquement.
Question: Pourquoi l’exemple ci-dessus fonctionne-t-il avec float
/ MPI_FLOAT
, mais pas avec double
/ MPI_DOUBLE
et comment puis-je résoudre ce problème?
Quelques spécificités de la machine qui pourraient être pertinentes:
Edit: comme suggéré dans les commentaires de Vladimir F, j’ai ajouté le code qui ne fonctionne pas.
Je viens de découvrir le problème: c’est bien un alignement. Que la deuxième liste de code produise correctement les 3 premiers doublons n’est qu’une étrange coïncidence … En utilisant l’extension de MPI_INT
comme décalage de la valeur suivante, j’imaginais qu’il n’y aurait pas de remplissage. Il est préférable de calculer les décalages comme ceci:
#include ... MPI_Datatype MY_ARRAY_TYPE; const int nr_blocks = 2; int blocklengths[2] = {1, 4}; MPI_Datatype oldtypes[2] = {MPI_INT, MPI_DOUBLE}; MPI_Aint displacements[2]; displacements[0] = offsetof(My_array, row); displacements[1] = offsetof(My_array, elements); MPI_Type_create_struct(nr_blocks, blocklengths, displacements, oldtypes, &MY_ARRAY_TYPE); MPI_Type_commit(&MY_ARRAY_TYPE); ...
Je serais vraiment intéressé de voir comment cela pourrait fonctionner ainsi … pourquoi avons-nous 3 valeurs correctes et une 0,0? Puisque l’alignement était décalé de 4 octets et que les doublons sont représentés par 8 octets sur ma plate-forme, pourquoi n’ai-je pas obtenu des nombres aléatoires? Comment les 3 premiers peuvent-ils être correctement décodés s’ils prennent chacun les 4 octets inférieurs d’un double plus les 4 supérieurs du double suivant?