The MPI standard defines a family of data types MPI_X_INT
with X = FLOAT, DOUBLE, ...
. Let's stick with MPI_DOUBLE_INT
for definiteness. Its formal definition is:
(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);
The definition implies no padding between double
and int
. The standard also asserts that the total size of this type should be 16, the example reads (for char
instead of 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).
The corresponding C++ structure is struct DI { double d; int i; };
. This answer asserts that the struct
should be packed to avoid padding between double
and int
. But the size of a packed structure is 12 (assuming sizeof(int) = 4
), and it is impossible to use an array of them:
constexpr auto count = 2; // >1
DI_packed di[count] = {...};
MPI_Send(di, count, MPI_DOUBLE_INT, ...); // wrong!
Is there a C++ struct
that corresponds exactly to the MPI definition and that can be safely and portably used in an MPI code? It seems that the only guaranteed way to use struct
is to define a packed structure with manually added tail padding char
s. Is this correct?
As a side note, on my machine both MSVC and GCC generate the "MPI-compatible" layout for unpacked struct DI
, so this question might be irrelevant from a practical point of view, but I'm not sure.
Perhaps you can do this using union?
If you compile the following with g++ and run
#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;
}
you get
sizeof(Tmp) = 16
sizeof(Doubleint) = 16
sizeof(Packtmp) = 12
sizeof(Packdoubleint) = 16
i.e. the union variables (Doubleint and Packdoubleint) are always 16 bytes long even if the structures have different sizes - I've forced Packtmp to be unpadded using an attribute specific to g++.