Вирівнювання пам'яті: як використовувати alignof / alignas?


82

Зараз я працюю із спільною пам’яттю.

Я не можу зрозуміти alignofі alignas.

cppreference незрозумілий: alignofповертає "вирівнювання", але що таке "вирівнювання"? кількість байт для додавання для наступного вирівняного блоку? м'який розмір? Записи переповнення стека / блоги теж незрозумілі.

Хтось може пояснити чітко alignofі alignas?


1
cppreference намагається бути посиланням, а не підручником
Куббі

1
@Cubbi: ви також можете перевірити на cplusplus.com, є дебати, який сайт кращий, для певних тем cplusplus кращий, для інших cppreference кращий, я виявив, що обидва сайти певний час не є довгими
CoffeDeveloper

2
@DarioOO Я лише відповідав, чому cppreference не пояснює концепцію вирівнювання на alignofсторінці (це робиться зараз, на незавершеній об'єктній сторінці ). Я не розумію, наскільки cplusplus.com актуальний.
Cubbi

Відповіді:


82

Вирівнювання - це обмеження, при якому в пам'яті можна зберігати перший байт значення. (Це потрібно для підвищення продуктивності процесорів і дозволу використання певних інструкцій, які працюють лише з даними з певним вирівнюванням, наприклад, SSE потрібно вирівняти до 16 байт, тоді як AVX до 32 байт.)

Вирівнювання 16 означає, що адреси пам'яті, кратні 16, є єдиними дійсними адресами.

alignas

примусово вирівняти потрібну кількість байт. Ви можете вирівняти лише за степенями 2: 1, 2, 4, 8, 16, 32, 64, 128, ...

#include <cstdlib>
#include <iostream>

int main() {
    alignas(16) int a[4];
    alignas(1024) int b[4];
    printf("%p\n", a);
    printf("%p", b);
}

приклад виводу:

0xbfa493e0
0xbfa49000  // note how many more "zeros" now.
// binary equivalent
1011 1111 1010 0100 1001 0011 1110 0000
1011 1111 1010 0100 1001 0000 0000 0000 // every zero is just a extra power of 2

інше ключове слово

alignof

це дуже зручно, ви не можете зробити щось подібне

int a[4];
assert(a % 16 == 0); // check if alignment is to 16 bytes: WRONG compiler error

але ви можете зробити

assert(alignof(a) == 16);
assert(alignof(b) == 1024);

зауважте, що насправді це суворіше, ніж проста операція "%" (модуль). Насправді ми знаємо, що щось, вирівняне до 1024 байт, обов’язково вирівнюється до 1, 2, 4, 8 байтів, але

 assert(alignof(b) == 32); // fail.

Точніше кажучи, "alignof" повертає найбільшу ступінь 2, з якою щось вирівняно.

Також alignof - це приємний спосіб заздалегідь знати мінімальну вимогу вирівнювання для базових типів даних (він, ймовірно, поверне 1 для символів, 4 для float тощо).

Ще законно:

alignas(alignof(float)) float SqDistance;

Щось із вирівнюванням 16 тоді буде розміщено на наступній доступній адресі, кратній 16 (може бути неявне відступ від останньої використаної адреси).


10
На відміну від sizeof, alignofможна застосувати лише до type-id.
neverhoodboy

НЕ alignof()(і аналог alignas()) оцінювали під час компіляції, так що ніяких накладних витрат під час виконання?
безглуздість

ні. Це неможливо, компілятор може зробити це як оптимізацію у дуже небагатьох випадках, але загалом він не знатиме, як вирівнюються адреси пам'яті, перш ніж оцінювати 2 функції. Просто подивіться на збірку, згенеровану на моєму прикладі: goo.gl/ZbemBF
CoffeDeveloper

1
@Serthy Для уточнення alignof - це константа часу компіляції. alignasне є, і його доведеться підтримувати вашою реалізацією new(вимога стандарту) або спеціальним розподільником std .
Айдіякапі

Хороша відповідь, але вона потребує лікування structта членів структури, які є static. alignasвиявляється набагато вибагливішим, ніж __attribute__((aligned))особливо під час компіляторів, таких як Clang.
jww

11

Вирівнювання не є заповненням (хоча інколи вводять відступ для задоволення вимог до вирівнювання). Це химерна властивість типу C ++. Поклавши це в standarddese ( 3.11[basic.align])

Типи об’єктів мають вимоги до вирівнювання (3.9.1, 3.9.2), які обмежують адреси, за якими може бути виділений об’єкт цього типу. Вирівнювання - це ціле число, визначене реалізацією, що представляє кількість байтів між послідовними адресами, за якими може бути виділений даний об’єкт. Тип об’єкта накладає вимогу вирівнювання до кожного об’єкта цього типу; можна вимагати більш жорсткого вирівнювання за допомогою специфікатора вирівнювання (7.6.2).


1
Дуже цікаво. Не могли б ви навести кілька прикладів? Чи алігноф (структура X) == розмір (структура Х)? Чому ні ?
Offirmo

1
@Offirmo ні, за винятком випадковості: struct X { char a; char b}має розмір 2 і вимога до вирівнювання 1, в здорових системах (його можна розподілити за будь-якою адресою, оскільки символ можна виділити за будь-якою адресою)
Куббі,

вирівнювання вимога 1 ???? О, я розумію: я думав, що вирівнювання завжди було на "природних" 32-бітних / 64-бітних межах, але, мабуть, ні. Це пояснює речі ... Отже, на звичайних машинах результат alignof () завжди матиме максимум 4 (32 біти) або 8 (64 біти) Я правильно?
Offirmo

@Offirmo «природний» alignof буде максимум на alignof(std::max_align_t), що 16на моєму Linux (незалежно від того, є чи складання -m32 або -m64), але ви можете зробити це з суворішеalignas
Cubbi

7

Кожен тип має вимогу до вирівнювання. Як правило, це так, що до змінних цього типу можна отримати ефективний доступ, не вимагаючи, щоб центральний процесор генерував більше одного доступу для читання / запису, щоб досягти будь-якого даного члена типу даних. Крім того, це також забезпечує ефективне копіювання всієї змінної. alignofповерне вимогу вирівнювання для даного типу.

alignasвикористовується для примусового вирівнювання типу даних (до тих пір, поки він не буде менш жорстким, щоб alignofповернувся те, що вказаний тип даних)


3

Вирівнювання - це властивість, пов’язана з адресою пам’яті. Просто ми можемо сказати, що якщо адреса X вирівняна до Z, то x кратна Z, тобто X = Zn + 0. Тут головне, що Z - це завжди степені 2.

Вирівнювання - це властивість адреси пам'яті, що виражається числовою адресою за модулем потужності 2. Наприклад, адреса 0x0001103F за модулем 4 дорівнює 3. Ця адреса, як кажуть, вирівняна до 4n + 3, де 4 позначає обрану потужність 2. Вирівнювання адреси залежить від обраної потужності 2. Та сама адреса за модулем 8 дорівнює 7. Адреса називається вирівняною до X, якщо її вирівнювання дорівнює Xn + 0.

Вищевказане твердження міститься у довідковому файлі Microsoft c ++.

Якщо елемент даних зберігається в пам'яті з адресою, яка вирівнюється за його розміром, тоді цей елемент даних, як кажуть, є природним вирівнюванням , інакше не суміщений . Наприклад: якщо цілочисельна змінна розміром 4 байти зберігається в адресі, яка вирівняна до 4, тоді можна сказати, що змінна природно вирівняна, тобто адреса змінної повинна бути кратною 4.

Компілятори завжди намагаються уникати розбіжностей. Для простих типів даних адреси вибираються таким чином, що вони кратні розміру змінної в байтах. Комплієр також підходить відповідно до структур для природного вирівнювання та доступу. Тут структура буде вирівняна до максимального розміру різних елементів даних у структурі. Наприклад:

    struct abc
   {
        int a;
        char b;
   };

Тут структура abc вирівняна до 4, що є розміром елемента int, який, очевидно, перевищує 1 байт (розмір елемента char).

алігнас

Цей специфікатор використовується для вирівнювання визначених користувачем типів, таких як структура, клас тощо, до певного значення, яке дорівнює 2.

алігноф

Це своєрідний оператор для отримання значення, до якого вирівняна структура або клас. наприклад:

#include <iostream>
struct alignas(16) Bar
{
    int i; // 4 bytes
    int n; // 4 bytes
    short s; // 2 bytes
};
int main()
{
    std::cout << alignof(Bar) << std::endl; // output: 16
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.