Le type de données dérivé de MPI fonctionne pour les floats, mais pas pour les doubles. Est-ce un problème d’alignement?

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:

  • CPU: AMD Opteron 6134
  • Tailles d’adresse: 48 bits
  • Alignement: 64
  • Compilateur: gcc 4.4.7
  • Bibliothèque MPI: (Malheureusement) spécifique au fournisseur

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?