Как выровнять элементы в массиве с помощью C++11 alignas
specifier
«Знаю ли я это уже?» Контрольный опрос
Как определить массив, чтобы каждый его элемент был выровнен по 64 байтам?
alignas(64) int option_1[4];
int CACHE_ALIGNED option_2[4];
int option_3[4] CACHE_ALIGNED;
typedef CACHE_ALIGNED int aligned_t;
aligned_t option_4[4];
Где макрос CACHE_ALIGNED
определяется следующим образом:
#if defined(__GNUC__) #define CACHE_ALIGNED __attribute__((aligned(64))) // clang and GCC #elif defined(_MSC_VER) #define CACHE_ALIGNED __declspec(align(64)) // MSVC #endif
Ответ в самом низу статьи.
Вариант использования: зачем выравнивать элементы массива?
Представьте себе массив состояний потоков: enum ThreadState state_flags[kMaxCPUs];
Рабочих потоков немного, и каждый поток читает и обновляет свое состояние: state_flags[this_thread_id] = kThreadAlive;
Синхронизация не требуется, так как к каждому элементу массива обращается исключительно один поток. Но в чем проблема с выравниванием здесь?
Ложное совместное использование — схема использования, снижающая производительность, когда одновременно запущенные потоки хранят свои личные данные в одной и той же строке кэша.
Чтобы избежать ложного совместного использования между рабочими процессами, каждый элемент должен быть выровнен по размеру строки кэша ЦП: 64 байта для процессоров x86 и 128 байтов для ARM. Давайте посмотрим, как выровнять элементы.
Анализ
Каждый полный тип объекта в C++ требует своего выравнивания. Стандарт C++11 удобно предлагает оператор alignof()
для запроса требования выравнивания данного типа:
/* The output on my system is 16 (long double) */ printf("Alignment of 'max_align_t' is %zd\n", alignof(max_align_t)); /* The output on my system is 4 */ printf("Alignment of 'int' is %zd\n", alignof(int));
Оба спецификатора alignas
и attribute((aligned))
(или declspec(align)
для MSVC) увеличивают требования к выравниванию, но могут применяться только к переменным или полям структуры. Невозможно увеличить требования к выравниванию для каждого элемента массива, поскольку требование выравнивания относится ко всему массиву:
alignas(64) int option_1[4];
Этот спецификатор alignas(64)
дает следующий результат:
Есть обходной путь. Мы можем использовать массив структур и выровнять поле в структуре или самой структуре:
struct AlignedField { alignas(64) int field; }; AlignedField solution_1[4]; struct alignas(64) AlignedStruct { int field; }; AlignedStruct solution_2[4];
Оба дают желаемый результат:
Недостатком является то, что каждый доступ к элементам массива теперь должен быть изменен с array[i]
на array[i].field
.
Еще одна плохая новость заключается в том, что поддержка ограничения больше alignof(max_align_t)
(16 в моей системе) определяется реализацией:
Если выравнивание объекта больше, чем max_align_t, для него требуется расширенное выравнивание. Структура, член которой имеет расширенное выравнивание, является типом с чрезмерным выравниванием. Он определяется реализацией, если поддерживаются типы с избыточным выравниванием.
Все современные компиляторы поддерживают типы с избыточным выравниванием. Это имеет смысл, поскольку общепринятой практикой является выравнивание данных как минимум по размеру строки кэша (64 или 128 байт).
Спецификатор alignas vs attribute((align)) vs declspec
В большинстве случаев они идентичны и делают одно и то же. Однако есть небольшая разница: alignas()
появляется в синтаксисе объявления как один из спецификаторов типа, поэтому приведенные ниже объявления недействительны:
// Invalid: not a type specifier int option_3[4] alignas(64); // Invalid: cannot be used in typedef typedef alignas(64) int aligned_t;
Спецификатор alignas
удобен, поэтому я предпочитаю использовать его, когда это возможно.
Выводы
- Каждый полный тип объекта в C++ требует своего выравнивания.
- Оператор
alignof
, поскольку C++11 должен запрашивать требование выравнивания. - Спецификатор
alignas
, так как C++11 увеличивает требования к выравниванию. - Требования к выравниванию применяются только к переменным и полям структуры.
Исходный код: https://github.com/berestovskyy/applied-cpp
Ответ на вопрос «Знаю ли я это уже?» Контрольный опрос
Ни один из ответов не является правильным. См. раздел «Анализ» для пояснений.