16

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
Harald
  • 3,110
  • 1
  • 24
  • 35

1 Answers1

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).

Community
  • 1
  • 1
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • 3
    `std::array` contains just the array of the elements. If you align it, you align the underlying array as well. – rustyx Aug 20 '16 at 21:42
  • That was what I was looking for. Interestingly, icpc didn't align `array` and `array`. – Harald Aug 20 '16 at 21:47
  • I often use the following construct: `template struct __attribute__((aligned(16))) aligned_array : public std::array {};` In this way I don't need to write `alignas` everywhere. – Jens Munk Aug 20 '16 at 21:52
  • @JensMunk You may also do: `template struct alignas( 16 ) aligned_array {};` – user2296177 Aug 21 '16 at 05:14
  • @user2296177 No, that makes no sense. The portable way is `template struct alignas(16) aligned_array : public std::array {};`. MSVC 2013 does not support this, so I didn't bother making it portable. I have a version for GCC and another for MSVC – Jens Munk Aug 21 '16 at 07:35
  • @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! – user2296177 Aug 21 '16 at 15:40
  • Thanks for the Postscript @RustyX, it is very interesting. – Harald Aug 24 '16 at 08:55
  • GCC 6 and 7 actually will not always do the right thing. See http://stackoverflow.com/questions/43651923/gcc-fails-to-optimize-aligned-stdarray-like-c-array – John Zwinck Apr 27 '17 at 08:07
  • @Harald 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. – KeyC0de Sep 26 '19 at 10:59
  • "std::array contains just the array of the elements. If you align it, you align the underlying array as well": https://stackoverflow.com/questions/19103244/is-the-size-of-stdarray-defined-by-standard seems to suggest otherwise, you might want to comment there as well, I don't know which is correct. – Ciro Santilli OurBigBook.com Apr 23 '20 at 10:52
  • 1
    @CiroSantilli冠状病毒审查六四事件法轮功 see [\[class.mem\]/20](https://timsong-cpp.github.io/cppwp/n3337/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. – rustyx Apr 23 '20 at 11:15