TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.37K subscribers
Post #1027 3.69K
​​Выравнивание. А зачем?
#новичкам

Представим, что мы пишем что-то связанное с графикой. У нас есть плоскость черно-белых пикселей. Каждый пиксель определяется через цвет черно-белого тона и координаты на плоскости:

struct Pixel {
unsigned char grayscale;
int x, y;
};


Мы хотим сделать супер-мега-блезингово-быстрое приложение, которое при работе будет тратить всего 1 бит памяти. Ну или сколько получится, но как можно меньше. Поэтому мы паримся, как о перфомансе, так и о эффективности по памяти.

Если паримся, значит надо считать.

Посчитаем, сколько байт занимает структура Pixel. Это по сути sizeof(unsigned char) + 2 * sizeof(int). размер char'a 1 байт, размер int на современных десктопах 4 байта. С помощью применения дифференциального исчисления в пространстве Минковского получаем 9 байт.

Теория - это прекрасно, но надо все проверять:

std::cout << sizeof(Pixel) << std::endl;


И получаем..... 12 байт.

/+ 33% от расчетов. Вообще неоптимально получается. Но почему расчеты не сходятся с реальностью?

Дело вот в чём. Процессор читает данные из памяти не хаотично, а определёнными порциями, например, по 4, 8 байта за раз. Причём эти порции он читает только с определённых адресов. Например, 4-байтовое число int обычно нужно читать с адреса, который делится на 4. Чтобы понять, почему так, нужно лезть в устройство процессора и организации работы с памятью. Не будем так глубоко погружаться.

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

Иногда на между остановок вообще нельзя останавливаться. Некоторые процессоры этого не позволяют. Иногда можно. Например, можно прочитать int с адреса, который на 3 делится. Но тогда нужно читать 2 соседние порции и из их кусочков составлять нужное число. А это лишние операции и накладные расходы.

То есть высокая скорость обработки данных достигается в случае, когда адреса выровнены по нужной границе.

Теперь вернемся к Pixel. Представим, что мы создаем объект структуры:

Pixel p{255, 1, 1};


Исходя из рассуждений выше, логично предположить, что начало p будет лежать по уже выравненному адресу для более быстрого чтения. Допустим, он делится на 4. В начале структуры лежит 1 байт цвета. А после лежат 2 int'а по 4 байта, доступ к которым должен быть выровнен. Но если к числу, кратному 4-м, прибавить 1, то оно перестанет делиться на 4. То есть, если данные лежат прям последовательно, то доступ будет невыровненный, а значит медленный.

Но компилятор у нас - не тупая машина, питающаяся текстом программы. Он умный и умеет оптимизировать. Если для скорости работы программы нужно, что адреса x и y должны быть выровнены по четверке, то он сделает магию и выровняет их. Заклинание простое - добавляем 3 байта после поля grayscale и дело в шляпе! Вот поэтому на практике размер Pixel равен 12 байтам.

Ну ладно, мы еще повоюем за память. Попробуем перетасовать поля:

struct Pixel {
int x, y;
unsigned char grayscale;
};


Теперь-то будет 9 байт размер?!

Да нет. Все те же 12 байт.

Представьте, что вы сделали массив пикселей. Да, у первого элемента x и y выровнены по 4-ке. Но начиная со второго элемента выравнивание поедет к черту. Так не годится и приходят на помощь все те же 3 байта, только уже в конце структуры.

Кстати, эти дополнительные куски памяти, которые компилятор добавляет для обеспечения выравнивания, называются паддингами(padding). В них не хранятся данные, они нужны чисто для выравнивания.

Сегодня мы рассмотрели, фактически, только предназначение выравнивания. В следующих постах будем раскрывать подробности.

Align yourself. Stay cool.

#cppcore #optimization #compiler
  • ❤ 39
  • 👍 19
  • 🔥 7
  • ❤‍🔥 3
  • 😁 3
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →