Problème avec MPI_Type_create_struct

Je ne parviens pas à comprendre la méthode MPI_Type_create_struct. Disons que nous avons une structure:

struct foo(){ float value; char rank; } 

Et nous voulons envoyer cette structure à un autre processus. Considérez l’exemple de code ci-dessous:

 int count = 2; //number of elements in struct MPI_Aint offsets[count] = {0, 8}; int blocklengths[count] = {1, 1}; MPI_Datatype types[count] = {MPI_FLOAT, MPI_CHAR}; MPI_Datatype my_mpi_type; MPI_Type_create_struct(count, blocklengths, offsets, types, &my_mpi_type); 

Je ne suis pas sûr de ce que les offsets et les blocklengths font dans cet exemple. Quelqu’un peut-il expliquer ces deux parties ci-dessus?

Comme vous le savez, MPI_Type_create_struct() pour object de créer un moyen de créer le MPI_Datatype de MPI_Datatype l’utilisateur sur ses types structurés. Ces nouveaux types seront par la suite utilisables pour les communications MPI et les autres appels, tout comme les types par défaut, permettant par exemple de transférer des tableaux de structures de la même manière que vous transféreriez des tableaux d’ int ou de float s.

Voyons maintenant la fonction elle-même plus en détail.
Voici son résumé renvoyé par la commande man :

 NAME MPI_Type_create_struct - Create an MPI datatype from a general set of datatypes, displacements, and block sizes SYNOPSIS int MPI_Type_create_struct(int count, const int array_of_blocklengths[], const MPI_Aint array_of_displacements[], const MPI_Datatype array_of_types[], MPI_Datatype *newtype) INPUT PARAMETERS count - number of blocks (integer) --- also number of ensortinges in arrays array_of_types, array_of_displacements and array_of_blocklengths array_of_blocklengths - number of elements in each block (array of integer) array_of_displacements - byte displacement of each block (array of address integer) array_of_types - type of elements in each block (array of handles to datatype objects) OUTPUT PARAMETERS newtype - new datatype (handle) 

Voyons pour les parameters d’entrée si leur signification appelle des explications supplémentaires:

  • count : c’est assez clair, et dans votre cas, ce serait 2
  • array_of_types : eh bien, ce serait { MPI_FLOAT, MPI_CHAR } pour votre exemple
  • array_of_blocklengths : encore une fois, pas grand chose à dire. { 1, 1 } est ce dont vous avez besoin ici
  • array_of_displacements : c’est celui pour lequel il faut être un peu plus prudent. Il correspond aux décalages d’adresse de mémoire depuis le début de la structure, à l’adresse de chaque élément répertorié dans array_of_types . Dans votre cas, ce serait quelque chose comme { &f.value - &f, &f.rank - &f } , avec f étant de type foo . La partie délicate ici est que, en raison des contraintes d’alignement potentielles, vous ne pouvez pas être sûr que ce sera égal à { 0, sizeof( float ) } (même si je suis pratiquement sûr que ce sera le cas). Par conséquent, l’utilisation des décalages d’adresses comme indiqué montre que la méthode est entièrement portable. De plus (merci à Hristo Iliev de me l’indiquer), vous pouvez (et devriez) utiliser la macro offsetof() de stddef.h qui effectue exactement cette arithmétique de pointeur pour votre, simplifiant le code en { offsetof( foo, value ), offsetof( foo, rank ) } qui a l’air plus joli.

Avec les arguments initialisés de cette manière, l’appel à MPI_Type_create_struct() renverra un nouveau MPI_Datatype , qui conviendra pour l’envoi ou la réception d’ un foo à la fois. La raison en est que ce nouveau type ne prend pas en compte l’étendue réelle de la structure, y compris les contraintes d’alignement pour ses champs. Et votre exemple est parfait à cet égard puisqu’il sera (très probablement) creux.

La raison en est que les float ont en général une contrainte d’alignement de 32b, alors que les caractères n’en ont pas. Par conséquent, l’adresse de départ de la deuxième structure foo d’un tableau de thèmes n’est pas exacte à la fin de la première. C’est à la prochaine adresse mémoire alignée en 32b. Cela nous laissera un trou de 3 octets entre la fin d’un élément de la structure et le début du suivant dans le tableau.

Pour gérer ce problème, vous devrez redimensionner votre type afin de l’étendre avec MPI_Type_create_resized() , dont le résumé est le suivant:

 NAME MPI_Type_create_resized - Create a datatype with a new lower bound and extent from an existing datatype SYNOPSIS int MPI_Type_create_resized(MPI_Datatype oldtype, MPI_Aint lb, MPI_Aint extent, MPI_Datatype *newtype) INPUT PARAMETERS oldtype - input datatype (handle) lb - new lower bound of datatype (address integer) extent - new extent of datatype (address integer) OUTPUT PARAMETERS newtype - output datatype (handle) 

Son utilisation est assez facile, car lb et extend peuvent être récupérés en appelant directement une fonction spécialement conçue à cet effet, à savoir MPI_Type_get_extent() (mais vous pouvez aussi utiliser directement 0 et sizeof( foo ) ). De plus, étant donné que le type intermédiaire utilisé pour appeler MPI_Type_get_extent() et MPI_Type_create_resized() n’est pas utilisé dans une communication MPI réelle, il n’a pas besoin d’être MPI_Type_commit() avec MPI_Type_commit() , ce qui vous épargne certains appels et votre temps.

Maintenant, avec ça, votre code devient:

 int count = 2; int array_of_blocklengths[] = { 1, 1 }; MPI_Aint array_of_displacements[] = { offsetof( foo, value ), offsetof( foo, rank ) }; MPI_Datatype array_of_types[] = { MPI_FLOAT, MPI_CHAR }; MPI_Datatype tmp_type, my_mpi_type; MPI_Aint lb, extent; MPI_Type_create_struct( count, array_of_blocklengths, array_of_displacements, array_of_types, &tmp_type ); MPI_Type_get_extent( tmp_type, &lb, &extent ); MPI_Type_create_resized( tmp_type, lb, extent, &my_mpi_type ); MPI_Type_commit( &my_mpi_type );