MPI发送自定义C结构体时offsetof偏移异常及运行报错问题
偏移量不均匀的原因
这是C语言的结构体自然对齐规则导致的:
- 结构体中每个成员的起始偏移量必须是该成员自身大小的整数倍,你代码中的
int类型占4字节,所以frequecy的偏移量必须是4的倍数 - 当
WORD_SIZE=30时,word数组占30字节,30不是4的倍数,编译器会自动在word和frequecy之间插入2字节的填充位,所以frequecy的偏移量变成32而不是30 - 当
WORD_SIZE=40时,40是4的倍数,不需要插入中间填充位,所以frequecy的偏移量是40
MPI报错的原因
问题出在你创建的自定义MPI数据类型的扩展(extent,即MPI认为该类型单个元素占的内存长度)和C编译器计算的sizeof(Word)不一致:
- C语言要求整个结构体的总大小必须是结构体内部最大对齐成员大小的整数倍,64位系统下默认最大对齐单位通常是8字节
- 当
WORD_SIZE=40时,word(40字节)+frequecy(4字节)=44字节,44不是8的倍数,编译器会在结构体末尾补4字节的填充位,所以实际sizeof(Word)=48 - 你通过
MPI_Type_create_struct生成的MPI_MY_WORD默认的扩展是最后一个成员的偏移+最后一个成员的大小,即40+4=44,和实际的48不匹配 - 发送数组时,MPI会按照44字节的步长遍历数组元素,而C数组的元素步长是48字节,从第二个元素开始就会出现内存访问错位、越界,触发你看到的读取错误
- 当
WORD_SIZE=30时,word(30)+填充(2)+frequecy(4)=36字节,如果你的编译环境下最大对齐单位是4字节,36刚好是4的倍数,不需要末尾填充,MPI类型的扩展32+4=36和sizeof(Word)刚好匹配,所以不会报错
解决方案
创建完结构体类型后,通过MPI_Type_create_resized手动调整自定义类型的扩展,使其和C编译器计算的结构体大小完全匹配,修改后的MPI类型注册代码如下:
MPI_Datatype types[2] = {MPI_CHAR, MPI_INT}; int blocklengths[2] = {WORD_SIZE,1}; MPI_Aint offsets[2] = { offsetof(Word, word), offsetof(Word, frequecy), }; MPI_Datatype MPI_MY_WORD_TMP; MPI_Type_create_struct(2, blocklengths, offsets, types, &MPI_MY_WORD_TMP); // 调整类型的扩展为实际结构体大小 MPI_Aint lb; MPI_Aint real_extent = sizeof(Word); MPI_Type_get_extent(MPI_MY_WORD_TMP, &lb, NULL); MPI_Datatype MPI_MY_WORD; MPI_Type_create_resized(MPI_MY_WORD_TMP, lb, real_extent, &MPI_MY_WORD); MPI_Type_commit(&MPI_MY_WORD); MPI_Type_free(&MPI_MY_WORD_TMP); // 释放临时类型
该方案完全兼容不同平台、不同编译选项下的结构体对齐规则,不需要手动修改结构体对齐属性,也不会影响程序性能。
内容的提问来源于stack exchange,提问作者Neorem
相关产品推荐
相关产品推荐

