MPI_DOUBLE_INT 和 C++ 结构
MPI_DOUBLE_INT and C++ struct
MPI standard 定义了 MPI_X_INT
和 X = FLOAT, DOUBLE, ...
数据类型族。为了确定性,让我们坚持使用 MPI_DOUBLE_INT
。它的正式定义是:
(p. 180) The datatype MPI_DOUBLE_INT is as if defined by the following sequence of instructions:
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) Example 4.1 Assume that Type = {(double, 0), (char, 8)}
(a double
at displacement zero, followed by a char
at displacement eight). Assume, furthermore, that double
s have to be strictly aligned at addresses that are multiples of eight. Then, the extent of this datatype is 16 (9 rounded to the next multiple of 8).
对应的C++结构为struct DI { double d; int i; };
。 断言 struct
应该被打包以避免 double
和 int
之间的填充。但是packed structure的大小是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
的打包结构。这是正确的吗?
附带说明一下,在我的机器上,MSVC 和 GCC 都会为未打包的 struct DI
生成 "MPI-compatible" 布局,所以从实际的角度来看,这个问题可能无关紧要,但我不确定。
也许你可以使用 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。
您可以使用 C++11's alignas keyword to fix alignment (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 and 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 standard 定义了 MPI_X_INT
和 X = FLOAT, DOUBLE, ...
数据类型族。为了确定性,让我们坚持使用 MPI_DOUBLE_INT
。它的正式定义是:
(p. 180) The datatype MPI_DOUBLE_INT is as if defined by the following sequence of instructions:
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) Example 4.1 Assume that
Type = {(double, 0), (char, 8)}
(adouble
at displacement zero, followed by achar
at displacement eight). Assume, furthermore, thatdouble
s have to be strictly aligned at addresses that are multiples of eight. Then, the extent of this datatype is 16 (9 rounded to the next multiple of 8).
对应的C++结构为struct DI { double d; int i; };
。 struct
应该被打包以避免 double
和 int
之间的填充。但是packed structure的大小是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
的打包结构。这是正确的吗?
附带说明一下,在我的机器上,MSVC 和 GCC 都会为未打包的 struct DI
生成 "MPI-compatible" 布局,所以从实际的角度来看,这个问题可能无关紧要,但我不确定。
也许你可以使用 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。
您可以使用 C++11's alignas keyword to fix alignment (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 and 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]; \
...