Я уверен, что знаю. Библиотека матриц 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-байтовой выровненной структурой и другой.
Итак, мой второй вопрос такой же, как и мой первый: зачем мне выравнивания?





Why do I need alignas if the compiler aligns on its own?
Несколько причин вне моей головы:
Компилятор может быть настроен для упаковки структур, как описано здесь:
Заставить структуру C++ плотно упаковать
но вы хотите, чтобы какая-то конкретная структура имела выровненные члены
Вам нужно выравнивание, выходящее за рамки того, что требует тип, например
alignas(1024) struct MyStruct
{
char m_c; // 1 byte
alignas(32) double m_x; // 8 bytes
};
Это могло быть связано с аппаратными ограничениями, например. у вас есть карта, которая может захватывать вещи из памяти с разрешением 1024 страницы; а затем на этой карте доступ к данным осуществляется блоками по 32 байта. Пример будет соответствовать этим требованиям, вместо того, чтобы вставлять фиктивные поля.
Набросок / нарезка типа: вы можете получить адрес массива из:
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.
Простой ответ заключается в том, что обычно вы этого не делаете, но если вам абсолютно необходимо работать с необработанной памятью и чужеродными структурами данных, вы можете найти alignas полезным. Большинство вещей, с которыми помогает alignas, раньше было возможно, но требовало усилий. Типичный пример - это что-то вроде
Struct* s = new (new alignas(Struct) char [ sizeof(Struct)]) Struct, но вам нужно нужно, чтобы создавать объекты на месте в необработанной памяти.