为包含指针的结构创建MPI_Datatype

我有以下结构。

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

我想使用MPI_SendMPI_Receive传输此结构。 如何为此结构创建MPI_Datatype

MPI旨在使用结构数组而不是数组结构。

@suszterpatt提出的MPI_Hindexed是一个可怕的黑客。 它只允许您发送结构类型的一个元素,并且只发送用于定义MPI数据类型的元素。 对于相同结构类型的其他变量,大多数保证计算的偏移量是错误的。 除了Hindexed类型之外,对所有元素使用同一个MPI数据类型,因此不允许您发送整数和双精度数。

明智的做法是将程序转换为使用结构数组:

  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 ,它描述结构成员的布局,但不考虑最后的任何填充,因此不能用于可靠地发送这种结构的数组。 因此,使用MPI_Type_create_resized创建具有正确范围的第二数据类型MPI_Type_create_resized

在接收方,您可以使用MPI_Probe查看消息,使用类型为point_type (直接转到nz字段)的MPI_Probe提取元素数,分配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-)编组 。 在你的情况下做这样的事情(我假设你在nz字段中存储AiAx中的数组元素的数量):

  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轻松地从消息的长度中提取它。

发送指向另一台机器的指针毫无意义(没有双关语)。 由于虚拟寻址,指针可能指向接收机器上的无效内存位置,即使没有,您实际上也没有发送它指向的数据。

但是,通过正确使用MPI_Address()MPI_Hindexed数据类型,可以描述数据的内存布局(我假设您的指针指向动态数组)。 例如,如果Ai指向3个int ,并且Ax指向5个double s,则需要具有3个块的Hindexed类型:3个MPI_DOUBLE s,5个MPI_DOUBLE和1个MPI_DOUBLE ,并使用MPI_Address()获取偏移量。

如果更改要发送的项目数或完全重新分配数组,请不要忘记重新定义并重新提交数据类型。 如果您要发送多个结构,则必须为每个结构定义并提交此数据类型,因为您的MPI数据类型特定于这些结构的一个特定实例。

另外请记住,如果要重新创建原始结构,则必须在接收端执行类似的棘手解包。

“明智的做法是将程序转换为使用结构数组”

通常,这在概念上也更好。

我想指出另一种机制:使用MPI_Pack和MPI_Unpack。 例如,使用原始结构,您可以打包第一个整数,然后打包两个数组。 接收器将解包整数,然后知道要打包的其他多少东西。

如果您的对象不能直接访问但只能通过迭代器访问,这也是一个很好的解决方案。