Производительность — один из важнейших аспектов хорошего программного обеспечения. По этой причине для достижения хорошей производительности используется множество методов оптимизации. Выравнивание данных, безусловно, является одним из них.
Что такое выравнивание данных?
Выравнивание данных — это размещение данных в памяти таким образом, чтобы ЦП мог более эффективно обращаться к ним. Например, 32-разрядный ЦП может более эффективно обращаться к данным, если данные организованы в виде 4-байтовых слов, расположенных по адресу, который делится без остатка на размер данных.
Структуры выделяются как непрерывный блок памяти, где каждый элемент имеет собственный размер, смещение и выравнивание. Давайте создадим структуру Article и посмотрим, как компилятор определяет размещение ее элементов. Предполагается 64-битный процессор.
struct Article { char *title; char *author; char *body; bool is_published; bool is_member_only; int no_comments; int no_claps; };
Структуры выравниваются в соответствии с естественными требованиями выравнивания самого большого элемента. Это выравнивание достигается путем разделения выделенной памяти на сегменты одинакового размера. Размер каждого сегмента соответствует максимальному выравниванию.
Структура Article выровнена по 8 байтам, что соответствует выравниванию типа указателя char с максимальным выравниванием. Для правильного выравнивания каждый элемент в структуре должен быть размещен со смещением, соответствующим его естественному выравниванию. Для этого может потребоваться вставка байтов заполнения между элементами структуры.
Обратите внимание, что перед no_comments есть 2 байта заполнения. Это связано с тем, что no_comments выравнивается по 4 байтам и сразу следует за is_published и is_member_only, которые занимают 2 байта пространства, поэтому было добавлено еще 2 байта, чтобы обеспечить естественное выравнивание no_comments. Также обратите внимание, что 4 байта заполнения добавляются в конец после члена no_claps, чтобы сформировать полный 8-байтовый сегмент.
Отсутствие отступов для уменьшения размера
Требования к выравниванию увеличивают размер структур. Это нормально, если у вас много оперативной памяти, но в среде с ограничениями, такой как микроконтроллер, у вас может не хватить памяти. Один из способов обойти это — подсказать компилятору выполнить сжатие, когда это возможно, с помощью упакованного атрибута.
struct Article { char *title; char *author; char *body; bool is_published; bool is_member_only; int no_comments; int no_claps; }__attribute__((packed));
Упакованный атрибут указывает компилятору размещать элементы структуры без заполнения байтов между ними. Это уменьшает размер структуры, но не гарантирует выравнивания данных и может снизить производительность.
Упакованные структуры требуют больше работы ЦП, чтобы получить оставшиеся байты. Это четко отражено в длине сгенерированного ассемблерного кода для обычных и упакованных структур.
/* gcc X84-64 assembly code for calling the function "Article_Copy" on both regular and packed structs. Assembly code generated using godbolt.org */ // Regular struct main: push rbp mov rbp, rsp sub rsp, 48 mov QWORD PTR [rbp-48], mov QWORD PTR [rbp-40], 0 mov QWORD PTR [rbp-32], 0 mov QWORD PTR [rbp-24], 0 mov QWORD PTR [rbp-16], 0 push QWORD PTR [rbp-16] push QWORD PTR [rbp-24] push QWORD PTR [rbp-32] push QWORD PTR [rbp-40] push QWORD PTR [rbp-48] call Article_Copy add rsp, 40 mov eax, 0 leave ret // Packed struct main: push rbp mov rbp, rsp push rbx sub rsp, 56 mov QWORD PTR [rbp-64], 0 mov QWORD PTR [rbp-56], 0 mov QWORD PTR [rbp-48], 0 mov QWORD PTR [rbp-40], 0 mov WORD PTR [rbp-32], 0 sub rsp, 40 mov rax, rsp mov rcx, QWORD PTR [rbp-64] mov rbx, QWORD PTR [rbp-56] mov QWORD PTR [rax], rcx mov QWORD PTR [rax+8], rbx mov rcx, QWORD PTR [rbp-48] mov rbx, QWORD PTR [rbp-40] mov QWORD PTR [rax+16], rcx mov QWORD PTR [rax+24], rbx movzx edx, WORD PTR [rbp-32] mov WORD PTR [rax+32], dx call Article_Copy add rsp, 40 mov eax, 0 mov rbx, QWORD PTR [rbp-8] leave ret
Переопределение выравнивания по умолчанию
Как упоминалось ранее, структуры выравниваются в соответствии с естественными требованиями выравнивания самого большого члена. Но что, если вы хотите применить определенное выравнивание к своей структуре? Для этого используйте выровненный атрибут вместе с упакованным атрибутом.
struct Article { char *title; char *author; char *body; bool is_published; bool is_member_only; int no_comments; int no_claps; }__attribute__((packed,aligned(4));
Структура Article теперь выровнена по 4 байтам вместо 8-байтового выравнивания типа указателя char. Обратите внимание, что компилятор может по-прежнему вставлять байты заполнения для обеспечения выравнивания данных.
Последнее примечание
Выравнивание структуры выполняется автоматически компилятором, поскольку ЦП намного быстрее получает доступ к выровненным данным. Выравнивание структуры достигается путем разделения пространства памяти структуры на сегменты одинакового размера. Размер каждого сегмента равен размеру наибольшего выравнивания. Для этого может потребоваться вставка байтов заполнения между элементами для обеспечения правильного выравнивания данных. Вы также можете указать пользовательское выравнивание, используя атрибутalign. В средах с ограничениями, где меньшие размеры структур предпочтительнее скорости, можно использовать упакованный атрибут, чтобы опустить заполнение и уменьшить размер структуры, но выравнивание данных не гарантируется.
Это все на данный момент.
Ваша поддержка и поддержка очень много значат для меня, поэтому, пожалуйста, подпишитесь на меня или купите мне кофе на Ko-fi.com, это так ценно!
Спасибо за чтение, и увидимся в следующем.
Повышение уровня кодирования
Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:
- 👏 Хлопайте за историю и подписывайтесь на автора 👉
- 📰 Смотрите больше контента в публикации Level Up Coding
- 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"
🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу