Создание типа MPI_Datatype для структуры, содержащей указатели

У меня есть следующая структура.

typedef struct
{
 int *Ai;
 double *Ax;
 int nz;
}column;

Я хочу передать эту структуру с помощью MPI_Send и MPI_Receive. Как создать MPI_Datatype для этой структуры?

Ответ 1

MPI предназначен для работы с массивами структур, а не со структурами массивов.

MPI_Hindexed, который предлагал @suszterpatt, - ужасный взлом. Он позволит вам отправлять только один элемент типа структуры и только элемент, который использовался для определения типа данных MPI. Для других переменных одного и того же типа структуры в большинстве случаев гарантируется, что вычисленные смещения будут ошибочными. Кроме того, типы Hindexed используют один и тот же тип данных MPI для всех элементов и, таким образом, не позволяют отправлять как ints, так и double.

Мудрое дело - превратить вашу программу в использование массивов структур:

 typedef struct
 {
    int i;
    double z;
 } point;

 typedef struct
 {
    point *A;
    int nz;
 } column;

Теперь вы можете создать структурированный тип MPI point_type и использовать его для отправки nz элементов этого типа, давая column.A в качестве адреса буфера:

 int lens[3];
 MPI_Aint base, disps[2];
 MPI_Datatype oldtypes[2], point_struct, point_type;

 MPI_Get_address(&point, disps);
 MPI_Get_address(&point.z, disps+1);
 base = disps[0];

 lens[0] = 1; disps[0] = MPI_Aint_diff(disps[0], base); oldtypes[0] = MPI_INT;
 lens[1] = 1; disps[1] = MPI_Aint_diff(disps[1], base); oldtypes[1] = MPI_DOUBLE;
 MPI_Type_create_struct(2, lens, disps, oldtypes, &point_struct);
 MPI_Type_create_resized(point_struct, 0, sizeof(point), &point_type);
 MPI_Type_commit(&point_type);

 MPI_Send(column.A, column.nz, point_type, ...);

Сначала создается тип данных MPI point_struct, который описывает компоновку элементов структуры, но не учитывает какие-либо дополнения в конце и, следовательно, не может использоваться для надежной отправки массива таких структур. Следовательно, второй тип данных point_type с правильной степенью создается с помощью MPI_Type_create_resized.

На стороне приемника вы заглянете в сообщение с помощью MPI_Probe, извлеките количество элементов с помощью MPI_Get_count с типом point_type (который идет прямо в поле nz), выделите A и используйте его в MPI_Recv для получения элементов nz:

 MPI_Status status;
 MPI_Probe(source, tag, comm, &status);
 MPI_Get_count(&status, point_type, &column.nz);
 if (nz == MPI_UNDEFINED)
   ... non-integral message was received, do something
 column.A = (point *)malloc(column.nz*sizeof(point));
 MPI_Recv(column.A, column.nz, point_type, source, tag, comm, MPI_STATUS_IGNORE);

Если это изменение кода невозможно, вы все равно можете пройти промежуточный шаг преобразования своей структуры перед отправкой, процесс, обычно называемый (un-) маршалинг. В вашем случае сделайте что-то вроде этого (я предполагаю, что вы храните количество элементов массива в Ai и Ax в поле nz):

 point *temp = (point *)malloc(nz*sizeof(point));
 for (int i = 0; i < column.nz; i++)
 {
   temp[i].i = column.Ai[i];
   temp[i].z = column.Az[i];
 }
 MPI_Send(temp, nz, point_type, ...);
 free(temp);

На стороне приемника вы должны сделать противоположное: выделить достаточно большой буфер, который может удерживать структуру, получать сообщение в ней и затем делать противоположное преобразование.

И снова вам не нужно передавать фактическое значение nz, так как его можно легко извлечь из длины сообщения, используя MPI_Get_count.

Ответ 2

Отправка указателей на другую машину бессмысленна (каламбур не предназначен). Из-за виртуальной адресации указатель, вероятно, укажет на недопустимое расположение памяти на принимающем компьютере, и даже если нет, вы фактически не отправили данные, на которые он указывал.

Однако при правильном использовании типов данных MPI_Address() и MPI_Hindexed можно описать макет памяти ваших данных (я предполагаю, что ваши указатели указывают на динамические массивы). Например. если Ai указывает на 3 int s, а Ax указывает на 5 double s, вам понадобится тип Hindexed с 3-мя блоками: 3 MPI_INT s, 5 MPI_DOUBLE s и 1 MPI_INT, с смещениями, полученными с помощью MPI_Address().

Не забудьте переопределить и подтвердить тип данных, если вы измените количество отправленных элементов или полностью перераспределите массивы. И если вы отправляете несколько структур, вам придется определять и фиксировать этот тип данных для каждого из них, так как ваш тип данных MPI специфичен для одного конкретного экземпляра этих структур.

Также имейте в виду, что вам нужно будет сделать подобную сложную распаковку на принимающей стороне, если вы хотите воссоздать исходную структуру.

Ответ 3

"Мудрое дело - превратить вашу программу в использование массивов структур"

Часто это концептуально также лучше.

Я хотел бы указать еще один механизм: использование MPI_Pack и MPI_Unpack. Например, с исходной структурой вы можете упаковать первое целое число, а затем упаковать два массива. Приемник распакует целое число, а затем узнает, сколько других вещей нужно распаковать.

Это также хорошее решение, если ваш объект недоступен напрямую, но доступен только через итератор или около того.