如何在MPI_Type_struct中包含动态数组?MPI混合代码开发问询
处理带动态数组的MPI自定义数据类型
好问题!首先得明确一个核心点:你修改后的struct st_Data(包含int *nData指针)无法直接用来定义MPI自定义数据类型。原因很简单:MPI传输的是实际数据内容,而指针存储的只是当前进程内存空间里的地址——这个地址在其他进程里完全无效,MPI也没办法自动追踪指针指向的动态数组内容。
不过我们有两种成熟的方案来解决这个问题,完全可以实现你的需求,下面详细说明:
方案1:分开传输结构体元数据和动态数组
这是最直观的方案,把结构体的固定部分(元数据)和动态数组的数据分成两次传输:
步骤1:修改结构体,增加数组长度字段
首先把原结构体里的指针换成数组的长度,这样接收方收到后就知道要分配多大的内存:
struct st_Data { int nWRank; int nSRank; char cName[160]; int nDataLen; // 新增:动态数组的元素个数 };
步骤2:定义并使用原风格的MPI类型
针对这个修改后的结构体,你可以用和之前几乎一样的方式定义MPI自定义类型(因为所有字段都是固定大小的):
MPI_Aint offsets[] = { (MPI_Aint)offsetof(struct st_Data, nWRank), (MPI_Aint)offsetof(struct st_Data, nSRank), (MPI_Aint)offsetof(struct st_Data, cName), (MPI_Aint)offsetof(struct st_Data, nDataLen) }; MPI_Datatype types[] = {MPI_INT, MPI_INT, MPI_CHAR, MPI_INT}; int blockcounts[] = {1, 1, 160, 1}; MPI_Datatype myMPI_Data_Meta; MPI_Type_struct(4, blockcounts, offsets, types, &myMPI_Data_Meta); MPI_Type_commit(&myMPI_Data_Meta);
步骤3:分两次传输
- 先传输结构体元数据(包含
nDataLen),接收方根据这个长度malloc出对应大小的nData数组; - 再单独传输动态数组
nData,用MPI_Send/MPI_Recv,或者根据你的场景用MPI_Scatterv/MPI_Gatherv(此时数组用MPI_INT类型即可)。
方案2:拼接连续内存块,定义复合MPI类型
如果你希望把结构体和动态数组作为一个整体传输,可以把它们拼接成连续的内存块,然后定义一个包含两部分的复合MPI类型:
步骤1:定义基础结构体(不含指针)
先把结构体里的指针去掉,只保留固定大小的字段:
struct st_Data_Base { int nWRank; int nSRank; char cName[160]; };
步骤2:分配连续内存,拼接结构体和数组
假设你已经从文件读取到动态数组的长度nDataLen,分配一块足够大的连续内存,前半部分放基础结构体,后半部分放动态数组:
int nDataLen = ...; // 从文件读取的数组长度 // 分配连续内存:基础结构体大小 + 数组总字节数 void *combined_buf = malloc(sizeof(struct st_Data_Base) + nDataLen * sizeof(int)); // 指向结构体部分的指针 struct st_Data_Base *base_data = (struct st_Data_Base*)combined_buf; // 指向动态数组部分的指针 int *nData = (int*)((char*)combined_buf + sizeof(struct st_Data_Base));
步骤3:定义复合MPI自定义类型
现在我们可以定义一个包含两个块的MPI类型:
MPI_Aint offsets[2]; MPI_Datatype types[2]; int blockcounts[2]; // 第一个块:基础结构体(直接按字节传输,或者也可以给基础结构体单独定义MPI类型) offsets[0] = 0; types[0] = MPI_BYTE; blockcounts[0] = sizeof(struct st_Data_Base); // 第二个块:动态数组 offsets[1] = sizeof(struct st_Data_Base); types[1] = MPI_INT; blockcounts[1] = nDataLen; MPI_Datatype myMPI_Data_Combined; MPI_Type_create_struct(2, blockcounts, offsets, types, &myMPI_Data_Combined); MPI_Type_commit(&myMPI_Data_Combined);
注意事项
- 这个MPI类型是依赖于
nDataLen的:如果不同进程的数组长度不同(比如MPI_Scatterv场景下每个进程的数据量不一样),你需要为每个不同的长度创建对应的MPI类型,或者在counts数组里对应调整每个进程的传输单元数; - 接收方必须提前知道
nDataLen才能正确分配内存——要么通过协议约定,要么先单独传输长度。
关键提醒
不管用哪种方案,绝对不要直接传输原结构体里的int *nData指针。指针是进程私有地址空间里的标识,其他进程拿到这个地址毫无意义,MPI也不会自动帮你传输指针指向的数据。
内容的提问来源于stack exchange,提问作者Dario D'Agostino
相关产品推荐
相关产品推荐

