Как выровнять 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;
}

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

1 ответ

Решение

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

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

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

Пост скриптум

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

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

Другие вопросы по тегам