Найти тему
Deep Software Engeneering

C++ alignof — что за зверь?

Неприятно признаться, но я никогда сильно не думал о такой вещи как alignment. В этой статье будем разбираться с основами вместе с читателями!

Итак, для начала стоит понять вообще что такое alignment. Несмотря на то, что в большинстве случаев в процессорах x86/amd64 доступ к памяти разрешен с точностью до конкретного байта, процессоры оптимизированы читать данные, начинающиеся с адресов, кратных машинному слову. Я написал простой бенчмарк.

Проверка чтения через границы машинного слова
Проверка чтения через границы машинного слова

И вот его результаты:

Результаты чтения с offset=0..8
Результаты чтения с offset=0..8

Видно, что есть падение производительности на несколько процентов. В особо экстремальных случаях, можно получить ситуации, когда данные будут располагаться в разных кэш-линиях или даже разных страницах памяти, и это будет иметь ещё более тяжелые последствия для производительности.

Поэтому компилятор старается учесть эти нюансы. Давайте посмотрим на то, что он может для этого делать.

Одним из инструментов, появившихся в C++11 стал оператор alignof. Он показывает, на какое число должен нацело делиться адрес, чтобы объект можно было разместить по этому адресу.

-3
  • alignof(char) = 1
  • alignof(int) = 4
  • alignof(int64_t) = 8
  • alignof(float) = 4
  • alignof(double) = 8

По соображениям совместимости , компилятор не может переупорядочивать поля в классах. Что дает интересные последствия:

-4

Смотрите, alignof(double) это 8. Таким образом, в MyStructA между переменными a и r компилятор оставит 4 байта мусора (padding). В случае же MyStructB, alignof(int) = 4, таким образом a и b расположатся один за другим. sizeof(MyStructA) = 24, sizeof(MyStructB) = 16. Неплохо, да?! Обычно, при создании новых классов пытаются это учесть.

Так же оператор alignof рассчитывает требования к адресу размещения для составных структур. Например:

-5

sizeof тут совпадают: по 16 байт. А вот alignof(MyStructC) = 4, в то время как alignof(MyStructD) = 8. Если нужно пояснить почему так выходит, напишите, пожалуйста, в комментарии.

Так же интересно заметить, что для локальных переменных C++ не гарантирует расположения переменных друг за другом. Только для структур и классов.

-6

Результат:
0x7ffee57583
80 0x7ffee5758388 0x7ffee5758390 (не смущайтесь, тут 88 + 8 = 90, мы в шестнадцатиричной системе).

Но в то же время:

-7

Выдает:
0x7ffedfd184
20 0x7ffedfd18418 0x7ffedfd18424, то есть компилятор разместил r перед a.

Надеюсь, вам была полезна эта статья! Если у Вас есть вопросы по С++ или Python, разбор которых хотелось бы увидеть на страницах этого канала, пожалуйста, не стесняйтесь попросить об этом в комментариях!

Наука
7 млн интересуются