MPI_DOUBLE_INT 和 C++ 结构
Posted
技术标签:
【中文标题】MPI_DOUBLE_INT 和 C++ 结构【英文标题】:MPI_DOUBLE_INT and C++ struct 【发布时间】:2019-01-22 13:56:27 【问题描述】:MPI standard 定义了一系列数据类型 MPI_X_INT
和 X = FLOAT, DOUBLE, ...
。让我们坚持使用MPI_DOUBLE_INT
来确定。它的正式定义是:
(p. 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);
该定义意味着double
和int
之间没有填充。该标准还断言该类型的总大小应为 16,示例为(char
而不是int
):
(p.85) 示例 4.1 假设
Type = (double, 0), (char, 8)
(位移为零的double
,接着是位移八的char
)。此外,假设double
s 必须严格对齐在 8 的倍数的地址上。然后,此数据类型的范围为 16(9 舍入为 8 的下一个倍数)。
对应的C++结构是struct DI double d; int i; ;
。 This answer 断言struct
应该被打包以避免double
和int
之间的填充。但是一个打包结构的大小是12(假设sizeof(int) = 4
),不可能使用它们的数组:
constexpr auto count = 2; // >1
DI_packed di[count] = ...;
MPI_Send(di, count, MPI_DOUBLE_INT, ...); // wrong!
是否有一个 C++ struct
与 MPI 定义完全对应并且可以在 MPI 代码中安全且可移植地使用?似乎唯一保证使用struct
的方法是定义一个带有手动添加尾部填充char
s 的打包结构。这是正确的吗?
附带说明一下,在我的机器上,MSVC 和 GCC 都为解压的struct DI
生成“MPI 兼容”布局,所以从实际角度来看,这个问题可能无关紧要,但我不确定。
【问题讨论】:
【参考方案1】:也许您可以使用 union 做到这一点?
如果你用 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。
【讨论】:
【参考方案2】:您可以使用C++11's alignas keyword 来修复对齐(@987654322@,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]; \
...
【讨论】:
以上是关于MPI_DOUBLE_INT 和 C++ 结构的主要内容,如果未能解决你的问题,请参考以下文章