Как выровнять данные, содержащиеся в std:: array?

С std::array не позволяет изменять его распределитель, есть ли способ гарантировать, что указатель на адрес данных выровнен?

например, в GNU g++ 4.8.4 и 6.1.0, код ниже

#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;
}

предоставляет следующие выходные данные, которые показывают, что данные контейнера выровнены по 16-байтовым адресам независимо от типа данных, содержащихся при компиляции для архитектуры x86-64 бит.

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

однако, для Intel icpc v16.0.3 результат показано ниже даже с помощью -align. Хотя большинство контейнеров выровнены по 16-байтовым адресам, некоторые (char и float массивы) выровнены по меньшим байтовым адресам (2-байтовым и 8-байтовым, соответственно).

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

редактировать

просто чтобы проиллюстрировать предложение от RustyX, это измененный код

#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;
}

и это результат при компиляции его с icpc V16 от Intel.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

1 ответов


по умолчанию компилятор будет делать правильные вещи, когда дело доходит до выравнивания.

но вы можете переопределить его с alignas:

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

Postscriptum

интересно, что компилятор Intel считает, что достаточно выровнять char[] на 8 байт. Как будто это знает что на платформе x86 вы мало выигрываете, выравнивая ее пошире.

имейте в виду, что слишком много выравнивание может уменьшить производительность из-за увеличения использования памяти и снижения эффективности кэша. Современные архитектуры x86 (Sandy Bridge и новее) очень эффективно работают с несогласованными данными, но не могут компенсировать частично используемые строки кэша (подробнее).