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_INTX = 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);

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

(p.85) 示例 4.1 假设 Type = (double, 0), (char, 8)(位移为零的double,接着是位移八的char)。此外,假设doubles 必须严格对齐在 8 的倍数的地址上。然后,此数据类型的范围为 16(9 舍入为 8 的下一个倍数)。

对应的C++结构是struct DI double d; int i; ;。 This answer 断言struct 应该被打包以避免doubleint 之间的填充。但是一个打包结构的大小是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 的方法是定义一个带有手动添加尾部填充chars 的打包结构。这是正确的吗?

附带说明一下,在我的机器上,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 来修复对齐(@9​​87654322@,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++ 结构的主要内容,如果未能解决你的问题,请参考以下文章

Swig - 结构的 C++ 向量以及如何连接它们

c++用while循环结构,输出1-10中所有的奇数

初识C++之程序流程结构

C++中,do while, for loop和while loop之间有啥不同

C++基础2(运算符程序结构流程数组)

在 C++ 中输入字符串 do while