Как выровнять 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 и более новые) работают очень эффективно с невыровненными данными, но не могут компенсировать частично используемые строки кэша ( подробнее).