Как работает alignas()?

314
20 января 2017, 07:55
int distance(void* first, void* second)
{
    return reinterpret_cast<int>(first) - reinterpret_cast<int>(second);
}
int main()
{
     alignas(16) int f[4];
     alignas(1024) int s[4];
     std::cout << distance(f, s);
     return 0;
}

Никак не могу понять как работает alignas(). Почему расстояние равно 2016, а без него 24?

Answer 1

alignas - это новая штука с с++11 стандарта и предназначена, чтобы устаканить то, что творится в разных компиляторах. Чтобы не говорили разработчики других языков, но иногда нужно писать немножко низкоуровневого кода, чтобы получить существенный плюс в скорости.

Как известно, процессоры лучше работают с данными в памяти, если они выровнены по границе в 4/8/16 байт. А в некоторых случаях данные обязаны быть выровнены (например, с некоторыми SSE командами и на некоторых ARM процессорах).

Что такое выравнивание по границе? Это просто адрес, который кратен заданной степени двойки. То есть, адрес 24 выровнен по границе 2, 4, 8, но не 16. Адрес 1024 выровнен по 2, 4, 8, 16, 32 и многим другим. Можно сказать и по-другому - адрес, выровненный по границе 16 в бинарном виде заканчивается на 4 нуля.

alignas как раз и заставляет компилятор сделать это выравнивание. Да, часто это идет в ущерб памяти (появляются пустоты), но в некоторых случаях (с SSE) можно получить двукратный прирост просто по той причине, что адрес выровнен по границе 16/32. Почему так происходит? Все просто - современные процессоры уже давно не загружают данные по байту из памяти - они обычно грузят как минимум по 4 байта сразу, более того, грузят по выровненным адресам по границе 4. И если нужно загрузить 4 невыровненных байта, то процессору приходится делать это в три этапа - загружаем первые байт (но при этом загружаем 4 байта), потом загружаем вторую половину, а потом складываем в памяти. А так как операция обращения к памяти - медленная - это длится достаточно долго.

Нужно ли в своем коде постоянно писать выравнивание? Нет, не нужно. Компиляторы достаточно умные и сами могут догадаться. Но есть случаи, когда это лучше сделать.

  • переменная используется для SSE/MMX.
  • переменная-массив, достаточно большого размера, по которому нужно будет много итерироваться (бегать).
  • числодробилки.

Почему расстояние равно 2016, а без него 24?

а на ideone вообще получилось -252:)

alignas говорит "выровняй", а как именно разместить в памяти - это другое дело. В Вашем случае компилятор вначале разместил первую переменную по адресу кратному 16. Вторую, нужно разместить по адресу кратному 1024. Логично, что такое может быть как сразу (потому что первая переменная занимает ровно 16 байт, так и через много-много байт (мы же не знаем адреса первой переменной).

Многие компиляторы в отладочном режиме за массивами всегда добавляют несколько байт - для того, что бы препятствовать классическому выходу за пределы массива на один элемент. Поэтому, чтобы понять, почему там именно 2016 - нужно смотреть в конкретную версию компилятора и параметры компиляции.

READ ALSO
Вылетает QWebEngineView

Вылетает QWebEngineView

ЗдравствуйтеПишу небольшой клиент вк и мне надо для авторизации открыть сайт для подтверждения

400
Как работает aligned_storage() С++11

Как работает aligned_storage() С++11

Прочитал про aligned_storage() на cppreference, но так и не смог понятьОбъясните, пожалуйста, попроще

321
Очистить экспортированный из Google Docs HTML

Очистить экспортированный из Google Docs HTML

Кто-нибудь знает способы лучше tidy-html для очистки очень грязного HTML после выгрузки из Google Docs? Интересуют именно программные способы (желательно...

307