align (C++)

В Visual Studio 2015 и более поздних версиях используйте alignas описатель (C++11) для управления выравниванием. Дополнительные сведения см. в разделе "Выравнивание".

Блок, относящийся только к системам Майкрософт

Используйте __declspec(align(#)) для точного управления выравниванием пользовательских данных (например, статическими распределениями или автоматическими данными в функции).

Синтаксис

__declspec( выравнивание(#) декларатор

Замечания

Написание приложений, использующих последние инструкции процессора, связано с некоторыми новыми ограничениями и проблемами. Многие новые инструкции требуют выравнивания данных с границами 16 байтов. Выравнивание часто используемых данных с размером строки кэша процессора повышает производительность кэша. Например, если определить структуру, размер которой меньше 32 байт, может потребоваться выравнивание 32-байтов, чтобы убедиться, что объекты этого типа структуры эффективно кэшируются.

# — это значение выравнивания. Допустимые записи — целые степени двух значений от 1 до 8192 (байты), например 2, 4, 8, 16, 32 или 64. declarator — это данные, которые вы объявляете как выровненные.

Сведения о том, как возвращать значение типа size_t , которое является требованием выравнивания типа, см. в разделе alignof. Сведения о том, как объявлять неуправляемые указатели при использовании 64-разрядных процессоров, см. в разделе __unaligned.

Можно использовать __declspec(align(#)) при определении structunion, или classпри объявлении переменной.

Компилятор не гарантирует или пытается сохранить атрибут выравнивания данных во время операции копирования или преобразования данных. Например, можно скопировать структуру, memcpy объявленную в __declspec(align(#)) любое расположение. Обычные распределители (например, mallocC++ operator newи распределители Win32) обычно возвращают память, которая не соответствует __declspec(align(#)) структурам или массивам структур. Чтобы гарантировать правильность выравнивания назначения операции копирования или преобразования данных, используйте _aligned_malloc. Или напишите свой собственный распределитель.

Невозможно указать выравнивание параметров функции. При передаче данных, которые имеют атрибут выравнивания по значению в стеке, соглашение о вызове управляет его выравниванием. Если в вызываемой функции важно выравнивание данных, скопируйте параметр в правильно выровненную память перед использованием.

Без __declspec(align(#))этого компилятор обычно сопоставляет данные по естественным границам на основе целевого процессора и размера данных, до 4-байтовых границ на 32-разрядных процессорах и 8-байтовых границ на 64-разрядных процессорах. Данные в классах или структурах выравниваются в классе или структуре по крайней мере его естественного выравнивания и текущего /Zp параметра упаковки (от #pragma pack или параметра компилятора).

В этом примере демонстрируется использование __declspec(align(#)):

__declspec(align(32)) struct Str1{
   int a, b, c, d, e;
};

Теперь этот тип содержит 32-разрядный атрибут выравнивания. Это означает, что все статические и автоматические экземпляры начинаются на 32-байтовой границе. Другие типы структур, объявленные с этим типом в качестве члена, сохраняют атрибут выравнивания этого типа. То есть любая структура с Str1 элементом имеет атрибут выравнивания не менее 32.

sizeof(struct Str1) Здесь равно 32. Это означает, что если создается массив Str1 объектов, а база массива выровнена по 32 байтам, каждый элемент массива также выравнивается по 32 байтам. Чтобы создать массив, база которого правильно выровнена в динамической памяти, используйте _aligned_malloc. Или напишите свой собственный распределитель.

Значение sizeof для любой структуры является суммой смещения последнего члена и его размера, округленной до ближайшего числа, кратного большему из следующих значений: значению выравнивания наибольшего члена или значению выравнивания всей структуры.

Компилятор использует эти правила для выравнивания структуры:

  • Если выравнивание не переопределяется с помощью __declspec(align(#)), выравнивание скалярного члена структуры — это его минимальный размер и текущая упаковка.

  • Если выравнивание не переопределяется с помощью __declspec(align(#)), выравнивание структуры — это максимальное число отдельных выравниваний членов.

  • Элемент структуры помещается в смещение с начала родительской структуры, которая является наименьшим из его выравнивания больше или равно смещением конца предыдущего элемента.

  • Размер структуры — это наименьшее число, кратное ее выравниванию, больше или равное смещению в конце его последнего члена.

__declspec(align(#)) может только увеличить ограничения выравнивания.

Дополнительные сведения см. в разделе:

align Примеры

В следующих примерах показано, как __declspec(align(#)) влияет на размер и выравнивание структур данных. В примерах допускаются следующие определения.

#define CACHE_LINE  32
#define CACHE_ALIGN __declspec(align(CACHE_LINE))

В этом примере структура S1 определена с помощью __declspec(align(32)). Все случаи использования S1 для определения переменных или в объявлениях других типов выравниваются по 32 байтам. sizeof(struct S1) возвращает значение 32, а S1 имеет 16 байтов заполнения после 16 байтов, необходимых для удержания четырех целых чисел. Для каждого int элемента требуется выравнивание 4-байтов, но выравнивание самой структуры объявляется 32. Затем общее выравнивание равно 32.

struct CACHE_ALIGN S1 { // cache align all instances of S1
   int a, b, c, d;
};
struct S1 s1;   // s1 is 32-byte cache aligned

В этом примере sizeof(struct S2) возвращает 16. Это сумма размеров членов, поскольку это число является кратным наибольшему требуемому выравниванию (кратное 8).

__declspec(align(8)) struct S2 {
   int a, b, c, d;
};

В следующем примере sizeof(struct S3) возвращает 64.

struct S3 {
   struct S1 s1;   // S3 inherits cache alignment requirement
                  // from S1 declaration
   int a;         // a is now cache aligned because of s1
                  // 28 bytes of trailing padding
};

Обратите внимание, что в этом примере a выравнивается по своему естественному типу, то есть, в данном случае, по 4 байтам. Однако значение S1 должно быть выровнено по 32-байтовой границе. 28 байт заполнений следуют a, чтобы s1 начинаться с смещения 32. S4 затем наследует требование S1выравнивания, так как это наибольшее требование выравнивания в структуре. sizeof(struct S4) возвращает 64.

struct S4 {
   int a;
   // 28 bytes padding
   struct S1 s1;      // S4 inherits cache alignment requirement of S1
};

Следующие три объявления переменных также используют __declspec(align(#)). В каждом случае переменная должна быть выровнена по 32 байтам. В массиве базовый адрес массива, а не каждый элемент массива, выравнивается по 32 байтам. Значение sizeof для каждого элемента массива не влияет при использовании __declspec(align(#)).

CACHE_ALIGN int i;
CACHE_ALIGN int array[128];
CACHE_ALIGN struct s2 s;

Для выравнивания каждого члена массива нужно использовать следующий код:

typedef CACHE_ALIGN struct { int a; } S5;
S5 array[10];

Обратите внимание, что в этом примере выравнивание самой структуры и первого элемента работают одинаково:

CACHE_ALIGN struct S6 {
   int a;
   int b;
};

struct S7 {
   CACHE_ALIGN int a;
               int b;
};

S6 и S7 имеют одинаковые характеристики выравнивания, выделения и размера.

В этом примере выравнивание начальных aадресов , bcа d также 4, 1, 4 и 1 соответственно.

void fn() {
   int a;
   char b;
   long c;
   char d[10]
}

Выравнивание при выделении памяти в куче зависит от того, какая функция выделения вызвана. Например, если используется malloc, результат зависит от размера операнда. Если arg>= 8, возвращенная память будет выровнена по 8 байтам. Если arg< 8, выравнивание возвращаемой памяти является первой мощностью 2 меньше arg. Например, если используется malloc(7), выравнивание равно 4 байтам.

Определение новых типов с помощью __declspec(align(#))

Можно определить тип с характеристикой выравнивания.

Например, можно определить struct значение выравнивания следующим образом:

struct aType {int a; int b;};
typedef __declspec(align(32)) struct aType bType;

aType Теперь и bType одинаковый размер (8 байтов), но переменные типа bType выровнены по 32 байтам.

Выравнивание данных в локальном хранилище потоков

Статическое локальное хранилище потока (TLS), созданное с помощью атрибута __declspec(thread) и помещенное в раздел TLS образа, обеспечивает выравнивание так же, как стандартные статические данные. Для создания данных TLS операционная система выделяет память в размере раздела TLS и сохраняет атрибут выравнивания раздела TLS.

В этом примере показаны различные способы помещения выровненных данных в локальное хранилище потока.

// put an aligned integer in TLS
__declspec(thread) __declspec(align(32)) int a;

// define an aligned structure and put a variable of the struct type
// into TLS
__declspec(thread) __declspec(align(32)) struct F1 { int a; int b; } a;

// create an aligned structure
struct CACHE_ALIGN S9 {
   int a;
   int b;
};
// put a variable of the structure type into TLS
__declspec(thread) struct S9 a;

Как align работает с упаковкой данных

Параметр /Zp компилятора и pack pragma влияют на упаковку данных для элементов структуры и объединения. В этом примере показано, как /Zp и __declspec(align(#)) работать вместе:

struct S {
   char a;
   short b;
   double c;
   CACHE_ALIGN double d;
   char e;
   double f;
};

В следующей таблице перечислены смещения каждого элемента в разных /Zp (или #pragma pack) значениях, в которых показано, как они взаимодействуют.

Переменная /Zp1 /Zp2 /Zp4 /Zp8
a 0 0 0 0
б 1 2 2 2
в 3 4 4 8
d 32 32 32 32
e 40 40 40 40
f 41 42 44 48
sizeof(S) 64 64 64 64

Дополнительные сведения см. в разделе /Zp (Выравнивание элементов структуры).

Смещение объекта зависит от смещения предыдущего объекта и от текущего параметра упаковки, если у объекта нет атрибута __declspec(align(#)). Если же такой атрибут есть, то выравнивание зависит от смещения предыдущего объекта и от значения __declspec(align(#)) объекта.

Завершение блока, относящегося только к системам Майкрософт

См. также

__declspec
Обзор соглашений ABI ARM
Программные соглашения для 64-разрядных систем