MPI_DOUBLE_INT and C++ struct

MPI_DOUBLE_INT and C++ struct

本文关键字:C++ struct and INT DOUBLE MPI      更新时间:2023-10-16

MPI标准定义了一系列MPI_X_INTX = FLOAT, DOUBLE, ...的数据类型。让我们坚持MPI_DOUBLE_INT确定性。其正式定义是:

"(第180页(数据类型MPI_DOUBLE_INT好像由以下指令序列定义:

block[0] = 1;
block[1] = 1;
disp[0] = 0;
disp[1] = sizeof(double);
type[0] = MPI_DOUBLE;
type[1] = MPI_INT;
MPI_TYPE_CREATE_STRUCT(2, block, disp, type, MPI_DOUBLE_INT);

该定义意味着doubleint之间没有填充。该标准还断言这种类型的总大小应为 16,示例如下(对于char而不是int(:

(第85页( 例4.1假设Type = {(double, 0), (char, 8)}(位移为零时double,位移 8 时char(。此外,假设double必须在 8 的倍数地址处严格对齐。然后,此数据类型的范围为 16(9 舍入到 8 的下一个倍数(。

相应的C++结构struct DI { double d; int i; };。这个答案断言,struct应该打包以避免在doubleint之间填充。但是一个填充结构的大小是 12(假设sizeof(int) = 4(,并且不可能使用它们的数组:

constexpr auto count = 2;  // >1
DI_packed di[count] = {...};
MPI_Send(di, count, MPI_DOUBLE_INT, ...);  // wrong!

是否有与 MPI 定义完全对应并且可以在 MPI 代码中安全、可移植地使用的C++struct?似乎唯一有保证的使用struct方法是定义一个带有手动添加尾部填充chars的填充结构。这是对的吗?

作为旁注,在我的机器上,MSVC 和 GCC 都为解压缩的struct DI生成"MPI 兼容"布局,因此从实际的角度来看,这个问题可能无关紧要,但我不确定。

也许你可以使用联合来做到这一点?

如果使用 g++ 编译以下内容并运行

#include <iostream>
using namespace std;
int main()
{
typedef struct {double x; int i;} Tmp;
typedef union {char pad[16]; Tmp dint;} Doubleint;
Doubleint value;
value.dint.x = 3.14;
value.dint.i = 6;
cout << "sizeof(Tmp)       = " << sizeof(Tmp)       << endl;
cout << "sizeof(Doubleint) = " << sizeof(Doubleint) << endl;
typedef struct {double x; int i;} __attribute__((packed)) Packtmp;
typedef union {char pad[16]; Packtmp dint;} Packdoubleint;
Packdoubleint packvalue;
packvalue.dint.x = 6.12;
packvalue.dint.i = 9;
cout << "sizeof(Packtmp)       = " << sizeof(Packtmp)       << endl;
cout << "sizeof(Packdoubleint) = " << sizeof(Packdoubleint) << endl;
return 0;
}

你得到

sizeof(Tmp)       = 16
sizeof(Doubleint) = 16
sizeof(Packtmp)       = 12
sizeof(Packdoubleint) = 16

即联合变量(Doubleint 和 Packdoubleint(始终为 16 字节长,即使结构具有不同的大小 - 我强制使用 g++ 特定的属性取消填充 Packtmp。

您可以使用C++11的alignas关键字来修复对齐方式(gcc 9.2,clang 9.0.0,icc 19.0.1(:

int main() {
#pragma pack(push, 1)
struct alignas(alignof(double)) pair {
double d;
int i;
};
#pragma pack(pop)
static_assert(sizeof(double) == 8, "");
static_assert(sizeof(int) == 4, "");
static_assert(sizeof(double)+sizeof(int) == 12, "");
static_assert(sizeof(pair) == 16, "");
static_assert(alignof(double) == 8, "");
static_assert(alignof(pair) == 8, "");
return 0;
}

如果没有alignas打包结构在 x86-64 Linux 上与 gcc 9.2、clang 9.0.0 和 icc 19.0.1 上存在对齐问题:

int main() {
#pragma pack(push, 1)
struct pair {
double d;
int i;
};
#pragma pack(pop)
static_assert(sizeof(double) == 8, "");
static_assert(sizeof(int) == 4, "");
static_assert(sizeof(double)+sizeof(int) == 12, "");
static_assert(sizeof(pair) == 12, "");
static_assert(alignof(double) == 8, "");
static_assert(alignof(pair) == 1, ""); // !!!
return 0;
}

顺便说一句:不确定,但OpenMPI可能能够在ompi/datatype/ompi_datatype_module.c中的数据类型定义期间处理非打包结构:

#define DECLARE_MPI2_COMPOSED_STRUCT_DDT( PDATA, MPIDDT, MPIDDTNAME, type1, type2, MPIType1, MPIType2, FLAGS) 
do {                                                                             
struct { type1 v1; type2 v2; } s[2];                                         
ompi_datatype_t *types[2], *ptype;                                           
int bLength[2] = {1, 1};                                                     
ptrdiff_t base, displ[2];                                                    
                
types[0] = (ompi_datatype_t*)ompi_datatype_basicDatatypes[MPIType1];         
types[1] = (ompi_datatype_t*)ompi_datatype_basicDatatypes[MPIType2];         
base = (ptrdiff_t)(&(s[0]));                                                 
displ[0] = (ptrdiff_t)(&(s[0].v1));                                          
displ[0] -= base;                                                            
displ[1] = (ptrdiff_t)(&(s[0].v2));                                          
displ[1] -= base;                                                            
                
ompi_datatype_create_struct( 2, bLength, displ, types, &ptype );             
displ[0] = (ptrdiff_t)(&(s[1]));                                             
displ[0] -= base;                                                            
if( displ[0] != (displ[1] + (ptrdiff_t)sizeof(type2)) )                      
ptype->super.ub = displ[0];                                              
...