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(#))
при определении struct
union
, или class
при объявлении переменной.
Компилятор не гарантирует или пытается сохранить атрибут выравнивания данных во время операции копирования или преобразования данных. Например, можно скопировать структуру, memcpy
объявленную в __declspec(align(#))
любое расположение. Обычные распределители (например, malloc
C++ 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(#))
- Выравнивание данных в локальном служба хранилища потока
- Как
align
работает с упаковкой данных - Примеры выравнивания структуры x64
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
адресов , b
c
а 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-разрядных систем
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по