How to align std::array contained data?
Asked Answered
E

1

17

Since std::array does not allow changing its allocator, is there a way to ensure that the pointer to the data address is aligned?

For instance, in GNU g++ 4.8.4 and 6.1.0, the code below

#include <array>
#include <iostream>

int main(void)
{
  std::array<bool, 10> a;
  std::array<char, 10> b;
  std::array<int,10> c;
  std::array<long long, 10> d;
  std::array<float, 10> e;
  std::array<double, 10> f;

  std::cout << "array<bool,10>.data()       = " << a.data() << std::endl;
  std::cout << "array<char,10>.data()       = " << (void*) b.data() << std::endl;
  std::cout << "array<int,10>.data()        = " << c.data() << std::endl;
  std::cout << "array<long long, 10>.data() = " << d.data() << std::endl;
  std::cout << "array<float, 10>.data()     = " << e.data() << std::endl;
  std::cout << "array<double, 10>.data()    = " << f.data() << std::endl;

  return 0;
}

provides the following output that shows that the container data is aligned to 16-byte addresses no matter the data-type contained when compiling for an x86-64 bit architecture.

array<bool,10>.data()       = 0x7ffe660a2e40
array<char,10>.data()       = 0x7ffe660a2e30
array<int,10>.data()        = 0x7ffe660a2e00
array<long long, 10>.data() = 0x7ffe660a2db0
array<float, 10>.data()     = 0x7ffe660a2d80
array<double, 10>.data()    = 0x7ffe660a2d30

However, for Intel's icpc v16.0.3 the result is shown below even using -align. While most of containers are aligned to 16-byte addresses, some (char and float arrays) are aligned to smaller byte addresses (2-byte and 8-byte, respectively).

array<bool,10>.data()       = 0x7ffdedcb6bf0
array<char,10>.data()       = 0x7ffdedcb6bfa
array<int,10>.data()        = 0x7ffdedcb6ba0
array<long long, 10>.data() = 0x7ffdedcb6b00
array<float, 10>.data()     = 0x7ffdedcb6bc8
array<double, 10>.data()    = 0x7ffdedcb6b50

EDIT

Just to exemplify the proposal from RustyX, this is the changed code

#include <array>
#include <iostream>

int main(void)
{
  alignas(16) std::array<bool, 10> a;
  alignas(16) std::array<char, 10> b;
  alignas(16) std::array<int,10> c;
  alignas(16) std::array<long long, 10> d;
  alignas(16) std::array<float, 10> e;
  alignas(16) std::array<double, 10> f;

  std::cout << "array<bool,10>.data()       = " << a.data() << std::endl;
  std::cout << "array<char,10>.data()       = " << (void*) b.data() << std::endl;
  std::cout << "array<int,10>.data()        = " << c.data() << std::endl;
  std::cout << "array<long long, 10>.data() = " << d.data() << std::endl;
  std::cout << "array<float, 10>.data()     = " << e.data() << std::endl;
  std::cout << "array<double, 10>.data()    = " << f.data() << std::endl;

  return 0;
}

and this is the result when compiling it with Intel's icpc v16.0.3.

array<bool,10>.data()       = 0x7ffe42433500
array<char,10>.data()       = 0x7ffe42433510
array<int,10>.data()        = 0x7ffe424334a0
array<long long, 10>.data() = 0x7ffe42433400
array<float, 10>.data()     = 0x7ffe424334d0
array<double, 10>.data()    = 0x7ffe42433450
Endocentric answered 20/8, 2016 at 21:26 Comment(3)
std::array doesn't work with any allocator. It just contains a member T data[N];.Nisa
Yes, I know @Nawaz. That's why I'm asking :)Endocentric
@RustyX thank you! I didn't know alignas. If you post an answer I'll accept it.Endocentric
D
19

By default the compiler will do the right thing when it comes to alignment.

But you can override it with alignas:

alignas(16) std::array<char, 10> b;

Postscriptum

It is interesting that the Intel compiler thinks it is sufficient to align a char[] on 8 bytes. It is as if it knows that on an x86 platform you gain little by aligning it any wider.

Keep in mind that too much alignment can reduce performance due to increased memory use and reduced cache efficiency. Modern x86 architectures (Sandy Bridge and newer) work very efficiently with unaligned data, but cannot compensate for partially used cache lines (more info).

Deem answered 20/8, 2016 at 21:40 Comment(11)
std::array contains just the array of the elements. If you align it, you align the underlying array as well.Deem
That was what I was looking for. Interestingly, icpc didn't align array<char> and array<float>.Endocentric
I often use the following construct: template <typename T, size_t N> struct __attribute__((aligned(16))) aligned_array : public std::array<T,N> {}; In this way I don't need to write alignas everywhere.Disoperation
@JensMunk You may also do: template<class T> struct alignas( 16 ) aligned_array {};Hooknose
@Hooknose No, that makes no sense. The portable way is template <typename T, size_t N> struct alignas(16) aligned_array : public std::array<T,N> {};. MSVC 2013 does not support this, so I didn't bother making it portable. I have a version for GCC and another for MSVCDisoperation
@JensMunk I omitted the inheritance to keep the comment short. Did you mean that something else made no sense? If so, please tell me. I'd like to know what you meant!Hooknose
Thanks for the Postscript @RustyX, it is very interesting.Endocentric
GCC 6 and 7 actually will not always do the right thing. See #43652423Manhole
@Endocentric Note that the first element of the array/aggregate type will be aligned to your chosen byte boundary (using alignas or a custom aligned allocator). The following elements might not be aligned to said boundary. Not that this would concern someone, as the first element is what is normally needed, just adding this comment for completeness and since I was a little confused about this matter myself.Recognition
"std::array contains just the array of the elements. If you align it, you align the underlying array as well": #19103744 seems to suggest otherwise, you might want to comment there as well, I don't know which is correct.Foin
@CiroSantilli冠状病毒审查六四事件法轮功 see [class.mem]/20: "A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member". So they must have the same address, and hence, the same alignment.Deem

© 2022 - 2024 — McMap. All rights reserved.