Неприятно признаться, но я никогда сильно не думал о такой вещи как alignment. В этой статье будем разбираться с основами вместе с читателями!
Итак, для начала стоит понять вообще что такое alignment. Несмотря на то, что в большинстве случаев в процессорах x86/amd64 доступ к памяти разрешен с точностью до конкретного байта, процессоры оптимизированы читать данные, начинающиеся с адресов, кратных машинному слову. Я написал простой бенчмарк.
И вот его результаты:
Видно, что есть падение производительности на несколько процентов. В особо экстремальных случаях, можно получить ситуации, когда данные будут располагаться в разных кэш-линиях или даже разных страницах памяти, и это будет иметь ещё более тяжелые последствия для производительности.
Поэтому компилятор старается учесть эти нюансы. Давайте посмотрим на то, что он может для этого делать.
Одним из инструментов, появившихся в C++11 стал оператор alignof. Он показывает, на какое число должен нацело делиться адрес, чтобы объект можно было разместить по этому адресу.
- alignof(char) = 1
- alignof(int) = 4
- alignof(int64_t) = 8
- alignof(float) = 4
- alignof(double) = 8
По соображениям совместимости , компилятор не может переупорядочивать поля в классах. Что дает интересные последствия:
Смотрите, alignof(double) это 8. Таким образом, в MyStructA между переменными a и r компилятор оставит 4 байта мусора (padding). В случае же MyStructB, alignof(int) = 4, таким образом a и b расположатся один за другим. sizeof(MyStructA) = 24, sizeof(MyStructB) = 16. Неплохо, да?! Обычно, при создании новых классов пытаются это учесть.
Так же оператор alignof рассчитывает требования к адресу размещения для составных структур. Например:
sizeof тут совпадают: по 16 байт. А вот alignof(MyStructC) = 4, в то время как alignof(MyStructD) = 8. Если нужно пояснить почему так выходит, напишите, пожалуйста, в комментарии.
Так же интересно заметить, что для локальных переменных C++ не гарантирует расположения переменных друг за другом. Только для структур и классов.
Результат:
0x7ffee5758380 0x7ffee5758388 0x7ffee5758390 (не смущайтесь, тут 88 + 8 = 90, мы в шестнадцатиричной системе).
Но в то же время:
Выдает:
0x7ffedfd18420 0x7ffedfd18418 0x7ffedfd18424, то есть компилятор разместил r перед a.
Надеюсь, вам была полезна эта статья! Если у Вас есть вопросы по С++ или Python, разбор которых хотелось бы увидеть на страницах этого канала, пожалуйста, не стесняйтесь попросить об этом в комментариях!