0

I have been testing Eigen library datatypes with union for type punning. My intent is to have a single memory of double array that can be accessed as an eigen data type and vice versa.

example:

union BigBox{
    double X[13];
    struct
    {
        Eigen::Vector3d p;
        Eigen::Vector3d v;
        Eigen::Vector3d w;
        Eigen::Vector4d q;
    } data;

};

When I test the

sizeof(BigBox)/sizeof(double) = 14
sizeof(Eigen::Vector3d)/sizeof(double) = 3
sizeof(Eigen::Vector4d)/sizeof(double) = 4

The size of the struct does not add up. How does the extra +1 get allotted? I believe it could be because of the compiler trying to exploit the SMID features but is there any way for me to use type punning in these situations? What is the correct approach for what I am trying to achieve?

  • Nothing to do with SIMD, the compiler has padded it to a multiple of 16 bytes, i.e. allocated an anonymous extra 8 bytes at the end of the struct. This is a compiler dependent behaviour, but generally if your struct > 2 bytes it will be padded to a multiple of 2, if > 4 bytes, to a multiple of 4 and so on. – Ben Dec 29 '18 at 00:13
  • Thank you @Ben, but i realized that based on the order of my vectors inside the struct the position of this padding varies. ie if I try to load X with a sequence from 1 to 13 and read it via p,v,w,q, the missing number is not always the same. Is there anyway to specify padding bytes location? – user2057361 Dec 29 '18 at 00:26
  • Put in explicit padding: `Eigen::Vector3d p; double unused1; Eigen::Vector3d v; double unused2;` and so on. – Ben Dec 29 '18 at 00:32
  • Also look in your compiler documentation for alignment directives since this is platform specific stuff. – Ben Dec 29 '18 at 00:34
  • E.g. for gcc https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html – Ben Dec 29 '18 at 00:35
  • 1
    Note I'm not familiar with Eigen - if these types have constructors or destructors then this is not safe to do. – Ben Dec 29 '18 at 00:36

1 Answers1

1

By default, Eigen::Vector4d is 16 byte aligned (or 32 byte aligned when compiled with AVX, to be more precise it will be aligned to EIGEN_MAX_STATIC_ALIGN_BYTES). That means after 3 vectors of 3*8 bytes each (=72 bytes), there will be 8 padding bytes. You can work-around it by either putting the most aligned element at the start, or by locally disabling alignment.

None of this is really safe, since in C++ unions should not be used for type-punning -- although it often works in practice.

To be a bit more safe, you can do something like this:

struct BigBox{
    Eigen::Matrix<double,13,1> X;

    Eigen::Ref<Eigen::Vector3d      > p()       { return X.segment<3>(0); }
    Eigen::Ref<Eigen::Vector3d const> p() const { return X.segment<3>(0); }
    Eigen::Ref<Eigen::Vector3d      > v()       { return X.segment<3>(3); }
    Eigen::Ref<Eigen::Vector3d const> v() const { return X.segment<3>(3); }
    Eigen::Ref<Eigen::Vector3d      > w()       { return X.segment<3>(6); }
    Eigen::Ref<Eigen::Vector3d const> w() const { return X.segment<3>(6); }
    Eigen::Ref<Eigen::Vector4d      > q()       { return X.segment<4>(9); }
    Eigen::Ref<Eigen::Vector4d const> q() const { return X.segment<4>(9); }

};

chtz
  • 17,329
  • 4
  • 26
  • 56