﻿# Урок 23\. Паттерн 15\. Рост размеров структур

Сам по себе рост размера структур не является ошибкой, но может приводить к потреблению необоснованного количества памяти и в результате к замедлению скорости работы программы\. Будем рассматривать данный паттерн не как ошибку, но как причину неэффективности 64\-битного кода\.

Данные в структурах в языке Си\+\+ выравниваются таким образом, чтобы обеспечить более эффективный к ним доступ\. Некоторые микропроцессоры вообще не могут напрямую обращаться к не выровненным данным и компилятору приходиться генерировать специальный код для обращения к таким данным\. Те же микропроцессоры, которые могут обращаться к не выровненным данным, все равно делают это намного менее эффективно\. Поэтому компилятор Си\+\+ оставляет пустые ячейки между полями структур, чтобы обеспечить их выравнивание по адресам машинных слов и тем самым ускорить к ним обращение\. Можно отключить выравнивание, используя специальные директивы \#pragma, чтобы сократить объем используемой памяти, но нас этот вариант сейчас не интересует\. Часто можно значительно сократить объем расходуемой памяти простым изменением порядка полей в структуре, без потери производительности\.

Рассмотрим следующую структуру:

```cpp
struct MyStruct
{
  bool m_bool;
  char *m_pointer;
  int m_int;
};
```

На 32\-битной системе эта структура займет 12 байт, и сократить этот размер не представляется возможным\. Каждое поле выровнено по границе 4 байта\. Даже если m\_bool перенести в конец, это ничего не изменит\. Компилятор все равно сделает размер структуры кратным 4 байтам для выравнивания таких структур в массивах\.

В случае 64\-битной сборки структура MyStruct займет уже 24 байта\. Это понятно\. В начале идет один байт под m\_bool и 7 неиспользуемых байт для выравнивания, так как указатель занимает 8 байт и должен быть выровнен по границе 8 байт\. Затем 4 байта для m\_int и 4 неиспользуемых байта, для выравнивания структуры по границе 8 байт\.

К счастью, дело можно легко поправить, переместив m\_bool в конец структуры, как показано ниже:

```cpp
struct MyStructOpt
{
  char *m_pointer;
  int m_int;
  bool m_bool;
};
```

Структура MyStructOpt займет уже не 24, а 16 байт\. Визуально расположение полей представлено на рисунке 1\. Весьма существенная экономия, если мы будем использовать, например, 10 миллионов элементов\. В этом случае мы сэкономим 80 мегабайт памяти, но что еще более важно, можем повысить производительность\. Если структур будет немного, то нет разницы, какого они размера\. Доступ будет происходить с одинаковой скоростью\. Но когда элементов много, то начинает играть роль кэш, количество обращений к памяти и так далее\. И можно с уверенностью утверждать, что обработка 160 мегабайт данных займет меньше времени, чем 240 мегабайт\. Даже простой доступ ко всем элементам массива для чтения, уже будет более быстр\.

![23_Pattern_15_Growth_of_structures_sizes_ru/image1.png](https://import.viva64.com/docx/blog/23_Pattern_15_Growth_of_structures_sizes_ru/image1.png)

_Рисунок 1 \- Расположение полей в структурах MyStruct и MyStructOpt_

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

Вы, наверное, зададите вопрос, по каким правилам компилятор выравнивает данные\. Мы ответим кратко, а если интересно познакомиться с этой темой более подробно, то мы отсылаем вас к книге Джеффри Рихтер \- Создание эффективных WIN32\-приложений с учетом специфики 64\-разрядной версии Windows\. Там этот вопрос рассматривается достаточно подробно\.

В целом правило выравнивание следующее: каждое поле выравнивается по адресу, кратному размеру данного поля\. Поле типа size\_t на 64\-битной системе будет выровнено по границе 8 байт, int по границе 4 байта, short по границе 2 байта\. Поля типа char не выравниваются\. Размер структуры выравнивается до размера, кратному размеру его максимального элемента\. Поясним это выравнивание на примере:

```cpp
struct ABCD
 {
  size_t m_a;
  char m_b;
 };
```

Элементы займут 8 \+ 1 \= 9 байт\. Но если размер структуры будет 9 байт, то, если мы захотим создать массив структур ABCD\[2\], поле m\_a второй структуры будет лежать по не выровненному адресу\. Вследствие этого компилятор дополнит структуру 7 пустыми байтами до размера 16 байт\.

Может показаться сложным процесс оптимизации последовательности полей\. Но можно предложить очень простой и очень эффективный способ\. Достаточно расположить поля в порядке убывания их размера\. Этого будет совершенно достаточно\. В этом случае поля начнут располагаться без лишних зазоров\. Например, возьмем следующую структуру размером 40 байт

```cpp
struct MyStruct
{
  int m_int;
  size_t m_size_t;
  short m_short;
  void *m_ptr;
  char m_char;
};
```

и простой сортировкой последовательности полей по убыванию размера:

```cpp
struct MyStructOpt
{
  void *m_ptr;
  size_t m_size_t;
  int m_int;
  short m_short;
  char m_char;
};
```

мы сделаем из нее структуру размером всего 24 байт\.

## Диагностика

Инструмент [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/) позволяет обнаружить структуры в коде 64\-битных приложений, перестановка полей в которых, позволит сократить их размер\. На неоптимальные структуры анализатор выдает диагностическое сообщение [V802](https://pvs-studio.ru/ru/docs/warnings/v802/)\.

Анализатор не всегда выдает сообщение о неэффективности структур, так как старается сократить количество излишних предупреждений\. Например, анализатор не выдает предупреждение на сложные классы, являющимися наследниками, поскольку такие объекты обычно создаются в малом количестве\. Пример:

```cpp
class MyWindow : public CWnd {
  bool m_isActive;
  size_t m_sizeX, m_ sizeY;
  char m_color[3];
  ...
};
```

Размер данной структуры может быть сокращен, но это не имеет практического смысла\.

_Авторы курса: Андрей Карпов \([karpov@viva64\.com](mailto:karpov@viva64.com)\), Евгений Рыжков \([evg@viva64\.com](mailto:evg@viva64.com)\)\._

_Правообладателем курса "Уроки разработки 64\-битных приложений на языке Си/Си\+\+" является ООО "Системы программной верификации"\. Компания занимается разработкой программного обеспечения в области анализа исходного кода программ\. [Сайт компании](https://pvs-studio.ru/ru/pvs-studio/)\._