Проблема с пониманием типа MPI create struct

у меня возникли проблемы с пониманием метода MPI_Type_create_struct. Скажем, у нас есть структура:

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

и мы хотим отправить эту структуру в другой процесс. Рассмотрим пример кода ниже:

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);

Я не уверен, что зачеты и blocklengths сделать в этом примере. Может кто-нибудь объяснить эти две части выше?

1 ответов


цель MPI_Type_create_struct() - это, как вы знаете, чтобы обеспечить способ создания отображение его структурированных типов. Эти новые типы впоследствии будут использоваться для связи MPI и других вызовов так же, как типы по умолчанию, позволяя, например, передавать массивы структур так же, как вы бы передать массивы intили floats.

теперь давайте посмотрим на саму функцию более подробно.
Вот его синопсис, возвращенный 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 entries
             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)

давайте посмотрим для входных параметров, если их значение требует дальнейшего объяснения:

  • count: это совершенно ясно, и в вашем случае это было бы 2
  • array_of_types: ну, это было бы
  • array_of_blocklengths: опять же, не так много сказать. { 1, 1 } это то, что вам нужно здесь
  • array_of_displacements: это тот, для которого вы должны быть немного более осторожны. Это соответствует смещение адреса памяти от начала структуры до адреса каждого элемента, указанного в array_of_types. В вашем случае это было бы что-то вроде { &f.value - &f, &f.rank - &f } С f тип foo. Сложная часть здесь заключается в том, что из-за потенциальных ограничений выравнивания вы не можете быть уверены, что это будет равно { 0, sizeof( float ) } (хотя здесь я уверен, что это будет). Поэтому использование смещений адресов, как показано, делает метод полностью переносимым. Более того (thx Христо Илиев чтобы указать на меня), вы можете (и должны) использовать offsetof() макрос stddef.h который делает именно эту арифметику указателя для вашего, упрощая код до { offsetof( foo, value ), offsetof( foo, rank ) } что выглядит лучше.

с аргументами, инициализированными таким образом, вызов MPI_Type_create_struct() возвращает новый MPI_Datatype, который будет подходить для отправки или получения один foo в то время. Причина этого заключается в том, что этот новый тип не учитывает фактический размер структура, включая ограничения выравнивания для ее полей. И ваш пример идеален в этом отношении, так как он (очень вероятно) будет пустым.

причина в том, что floats имеют в целом ограничение выравнивания 32b, в то время как charу s нет. Следовательно, начальный адрес второй структуры foo массива темы не является правильным в конце первого. Он находится на следующем 32B-выровненном адресе памяти. Это оставит нас с отверстием 3 байта между концом элемента структуры и началом следующего в массиве.

чтобы справиться с этой проблемой, вам придется изменить размер вашего типа для его расширения с помощью MPI_Type_create_resized(), синопсис которого выглядит следующим образом:

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)

используя его довольно легко, как оба lb и extend может быть получен путем прямого вызова функции, специально предназначенной для этой цели, а именно MPI_Type_get_extent() (но на самом деле, вы могли бы также использовать 0 и sizeof( foo )). Кроме того, поскольку тип посредника, используемый для вызова MPI_Type_get_extent() и MPI_Type_create_resized() не используется в какой-либо фактической связи MPI, это не должно быть совершено с MPI_Type_commit(), избавляя вас от некоторых звонков и времени.

теперь, с этим, ваш код становится:

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 );