我有以下结构。
typedef struct
{
int *Ai;
double *Ax;
int nz;
}column;
我想使用MPI_Send
and转移这个结构MPI_Receive
。我如何MPI_Datatype
为这个结构创建一个?
MPI 旨在处理结构数组而不是数组结构。
MPI_Hindexed
@suszterpatt 提出的那个是一个可怕的黑客攻击。它只允许您发送结构类型的一个元素,并且只允许发送用于定义 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
,用于描述结构成员的布局,但不考虑末尾的任何填充,因此不能用于可靠地发送此类结构的数组。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-)marshaling。在您的情况下,请执行以下操作(我假设您将数组元素的数量存储在两个字段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
。
向另一台机器发送指针是没有意义的(没有双关语的意思)。由于虚拟寻址,指针可能会指向接收机器上的无效内存位置,即使不是,您实际上也没有发送它所指向的数据。
但是,通过正确使用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 数据类型特定于这些结构的一个特定实例。
另请记住,如果您想重新创建原始结构,则必须在接收端进行一些类似的棘手解包。
“明智的做法是将程序转换为使用结构数组”
通常这在概念上也更好。
我想指出另一种机制:使用 MPI_Pack 和 MPI_Unpack。例如,使用原始结构,您可以打包第一个整数,然后打包两个数组。接收者将解包整数,然后知道要解包多少其他东西。
如果您的对象不能直接访问但只能通过迭代器左右访问,这也是一个很好的解决方案。