Есть ли проблемы с производительностью при использовании Pragma Pack (1)? - PullRequest
13 голосов
/ 17 октября 2011

Наши заголовки используют #pragma pack(1) вокруг большинства наших структур (используется для сетевого и файлового ввода-вывода).Я понимаю, что это меняет выравнивание структур со значения по умолчанию в 8 байтов до выравнивания в 1 байт.

Если предположить, что все работает в 32-битном Linux (возможно, также и в Windows), есть ли какое-либо снижение производительностичто происходит из-за выравнивания пакетов?

Меня не беспокоит переносимость библиотек, но больше касается совместимости файлового и сетевого ввода-вывода с различными пакетами #pragma и проблем с производительностью.

Ответы [ 7 ]

14 голосов
/ 17 октября 2011

Доступ к памяти самый быстрый, когда он может осуществляться по выровненным по словам адресам памяти.Простейшим примером является следующая структура (которую также использовал @Didier):

struct sample {
   char a;
   int b;
};

По умолчанию GCC вставляет заполнение, поэтому a имеет смещение 0, а b имеет смещение 4 (выравнивание по слову).Без дополнения b не выровнено по словам, и доступ медленнее.

Насколько медленнее?

  • Для 32-разрядных x86, в соответствии с Intel 64 иРуководство по разработке программного обеспечения IA32 Architectures :
    Процессору требуется два доступа к памяти, чтобы получить доступ к памяти без выравнивания;Выровненный доступ требует только одного доступа к памяти.Операнд из слова или двойного слова, пересекающий 4-байтовую границу, или операнд из четырех слов, пересекающий 8-байтовую границу, считается невыровненным и требует двух отдельных циклов шины памяти для доступа.
    Как и в большинстве вопросов производительности, вам придетсясравните ваше приложение, чтобы увидеть, насколько серьезна проблема на практике.
  • Согласно Википедии , расширения x86, такие как SSE2 , требуют выравнивания слов.
  • Многие другие архитектуры требуют выравнивания слов (и будут генерировать ошибки SIGBUS, если структуры данных не выровнены по словам).

Относительно переносимости: я предполагаю, что вы используете #pragma pack(1), чтобы вы моглиотправлять структуры по проводам и с диска, не беспокоясь о различных компиляторах или платформах, по-разному упаковывающих структуры.Это действительно так, однако, следует помнить о нескольких моментах:

  • Это ничего не дает для решения проблем с прямым и обратным порядком байтов.Вы можете справиться с этим, вызвав семейство функций htons для любых целых чисел, без знака и т. Д. В своих структурах.
  • По моему опыту, работа с упакованными, сериализуемыми структурами в коде приложения неочень весело.Их очень сложно модифицировать и расширять, не нарушая обратной совместимости, и, как уже отмечалось, существуют потери производительности.Рассмотрите возможность переноса содержимого упакованных, сериализуемых структур в эквивалентные неупакованные, расширяемые структуры для обработки или используйте полноценную библиотеку сериализации, такую ​​как Protocol Buffers (которая имеет C привязок ).
6 голосов
/ 17 октября 2011

Да.Там, безусловно, есть.

Например, если вы определяете структуру:

struct dumb {
    char c;
    int  i;
};

, то всякий раз, когда вы обращаетесь к члену i, ЦП замедляется, потому что 32-битное значение i не доступнородным, выровненным способом.Для простоты представьте, что ЦП должен получить 3 байта из памяти, а затем еще 1 байт из следующего расположения, чтобы передать значение из памяти в регистры ЦП.

3 голосов
/ 17 октября 2011

Когда вы объявляете структуру, большинство компиляторов вставляют байты заполнения между членами, чтобы гарантировать, что они выровнены по соответствующим адресам в памяти (обычно байты заполнения кратны размеру типа). Это позволяет компилятору оптимизировать доступ к этим элементам.

#pragma pack(1) указывает компилятору упаковать элементы структуры с определенным выравниванием. 1 здесь говорит компилятору не вставлять какие-либо отступы между членами.

Так что да, есть определенное снижение производительности , так как вы заставляете компилятор делать что-то помимо того, что он обычно делает для оптимизации производительности. Также, некоторые платформы требуют выравнивания объектов в специфические границы и использование невыровненных структур могут привести к ошибкам сегментации.

В идеале лучше избегать изменения правил естественного выравнивания по умолчанию. Но если директивы «Прагма-упаковка» вообще нельзя избежать (как в вашем случае), то после определения структур, требующих плотной упаковки, необходимо восстановить первоначальную схему упаковки.

Например:

//push current alignment rules to internal stack and force 1-byte alignment boundary
#pragma pack(push,1)  

/*   definition of structures that require tight packing go in here   */

//restore original alignment rules from stack    
#pragma pack(pop)
2 голосов
/ 17 октября 2011

Это зависит от базовой архитектуры и способа, которым он обрабатывает невыровненные адреса.

x86 изящно обрабатывает невыровненные адреса, хотя и за счет производительности, тогда как другие архитектуры, такие как ARM, могут вызывать ошибку выравнивания (SIGBUS), или даже "округлите" неверно выровненный адрес до ближайшей границы, и в этом случае ваш код будет сбои.невыровненные адреса и стоимость сетевых операций ввода-вывода выше стоимости обработки.

0 голосов
/ 29 июня 2019

На некоторых платформах, таких как ARM Cortex-M0, 16-битные инструкции загрузки / сохранения не будут работать, если используются по нечетному адресу, и 32-битные инструкции не будут работать, если они используются по адресам, не кратным четырем.Загрузка или сохранение 16-битного объекта с / на адрес, который может быть нечетным, потребует использования трех инструкций, а не одной;для 32-битного адреса потребовалось бы семь инструкций.

Для clang или gcc, взятие адреса элемента упакованной структуры даст указатель, который часто будет непригоден для доступа к этому элементу.На более полезном компиляторе Keil, взяв адрес члена структуры __packed, вы получите квалифицированный указатель __packed, который может быть сохранен только в объектах-указателях, которые также являются квалифицированными.При доступе через такие указатели будет использоваться последовательность из нескольких инструкций, необходимая для поддержки не выровненного доступа.

0 голосов
/ 17 октября 2011

Существуют определенные инструкции машинного кода, которые работают с 32-разрядным или 64-разрядным (или даже более), но ожидают, что данные будут выровнены по адресам памяти.Если это не так, им нужно выполнить более одной операции чтения / записи в памяти для выполнения своей задачи.Степень снижения производительности сильно зависит от того, что вы делаете с данными.Если вы строите большие массивы структур и выполняете обширные вычисления, они могут стать большими.Но если вы сохраняете данные только один раз, чтобы потом прочитать их, а затем преобразовать их в поток байтов, то это может быть едва заметным.

0 голосов
/ 17 октября 2011

Технически, да, это повлияет на производительность, но только в отношении внутренней обработки. Если вам нужны структуры, упакованные для ввода-вывода сети / файла, существует баланс между упакованным требованием и только внутренней обработкой. Под внутренней обработкой я подразумеваю работу, которую вы выполняете с данными между IO. Если вы выполняете очень мало обработки, вы не потеряете много с точки зрения производительности. В противном случае вы можете выполнить внутреннюю обработку на правильно выровненных структурах и только «упаковать» результаты при выполнении ввода-вывода. Или вы можете переключиться на использование только выровненных по умолчанию структур, но вам необходимо убедиться, что все выровняли их одинаково (сетевые и файловые клиенты).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...