Я уверен, что я делаю. Матричная библиотека Eigen3, вероятно, намного быстрее, чем любая из моих собственных реализаций матриц благодаря выравниванию.
Недавно я начал исследовать выравнивание с помощью C ++. Я наткнулся на функции alignas и alignof и решил провести несколько тестов.
Моя первая находка было то, что члены структуры выравниваются автоматически. Давайте возьмем следующую структуру в качестве примера:
struct MyStruct
{
char m_c; // 1 byte
double m_x; // 8 bytes
};
и сравните с этим:
struct MyAlignedStruct
{
char m_c; // 1 byte
alignas(8) double m_x; // 8 bytes
};
где я использовал alignas вместо добавления отступа (char [7]), что, по моему мнению, эквивалентно.
Теперь просмотрщик памяти для обеих структур показал следующее:
62 00 00 00 8e a8 79 35 00 00 00 00 00 00 10 40 // MyStruct
62 ff ff ff 24 00 00 00 00 00 00 00 00 00 10 40 // MyAlignedStruct
Первый байт соответствует символу (‘b’). При использовании Mystruct следующие 7 байтов заполняются что-то, и 8 последних байтов представляют двойное число. При использовании MyAlignedStruct происходит нечто очень похожее. Функция sizeof () возвращает 16 байтов для обеих структур (я ожидал 9 байтов для MyStruct).
Итак, вот мой первый вопрос: зачем мне выровнять, если компилятор выравнивается сам?
Моя вторая находка было то, что alignas (..) не ускоряет мою программу. Мой эксперимент был следующим. Представьте себе следующую простую структуру:
struct Point
{
double m_x, m_y, m_z;
};
Если я заполню вектор экземплярами этой структуры и предположим, что первый экземпляр выровнен по 32 байта, каждая структура будет занимать 24 байта, а последовательность байтов больше не будет выровнена по 32 байта. Честно говоря, я не уверен, каким образом скорость может быть увеличена путем выравнивания, иначе я бы, скорее всего, не писал здесь. Тем не менее, я использовал alignas для получения следующей структуры:
alignas(32) struct Point
{
double m_x, m_y, m_z;
};
Теперь непрерывные экземпляры Point будут начинаться с кратности 32 байта. Я протестировал обе версии: после заполнения огромного вектора экземплярами структур я суммировал все двойные числа и записал время. Я не нашел различий между 32-байтовой выровненной структурой и другой.
Итак, мой второй вопрос такой же, как и мой первый: зачем мне использовать alignas?
Зачем мне выравнивание, если компилятор выравнивается сам по себе?
Несколько причин из головы:
Компилятор может быть настроен для упаковки структур, как описано здесь:
Заставить структуру C ++ плотно упаковать
но вы хотите, чтобы какая-то конкретная структура имела выровненные элементы
Вы хотите выравнивание, которое выходит за рамки того, что требует тип, например,
alignas(1024) struct MyStruct
{
char m_c; // 1 byte
alignas(32) double m_x; // 8 bytes
};
Это может быть связано с аппаратными ограничениями, например, у вас есть карта, которая может захватывать вещи из памяти с разрешением 1024 страницы; и затем на этой карте доступ к данным осуществляется в единицах по 32 байта. Пример будет соответствовать этим требованиям вместо того, чтобы вставлять фиктивные поля.
Введите punning / slicing: вы можете получить адрес для массива:
struct s1
{
char m_c;
uint32_t m_d;
char m_e;
};
но я на самом деле использовал
struct s2
{
char m_c;
mystery_type_with_size_64_bits m_d;
char m_e;
};
так что даже если вы хотите работать с m_d
как uint32_t
, но вы также должны получить m_e
право.
Других решений пока нет …