LINUX.ORG.RU

★ #pragma pack VS #pragma не`pack ★

 , , ,


3

2

Расскажите про реальные минусы, плюсы. Подводные камни.

Ну упаковали мы структуру, ну стала она меньше памяти занимать. Как бы всё. Но допустим мы не экономим на памяти (допустим!) если ещё реальный смысл паковать? Где-то говорят что промахи кеша уменьшаются и от того код работает быстрее ведь как следствие конвеер не перезапускается, но я не верю.

UDP: На заметку (Всем спасибо за советы! (•◡•)/ )

Утилита pahole для выявления дыр в структурах и их реорганизации в Debian утилита доступна из пакета dwarves sudo apt install dwarves

gcc/clang c опцией -g3 -Wpadded + -Wextra -Wall -Werror не пропустят код с дырявыми структурами

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

typedef struct {
  bool collided;
  float time;
  vec3 point;
  vec3 norm;
  int flags;
} collision;


Clang выдаёт чуть более информативно

./include/physics.h:13:9: error: padding struct 'collision' with 3 bytes to align 'time' [-Werror,-Wpadded]
  float time;

GCC же просто указывает на предмет «проблемы»

./include/physics.h:13:9: error: padding struct to align ‘time’ [-Werror=padded]
   float time;
         ^~~~

Прогон pahole же при компиляции с -g3 даёт чёткое указание на «проблему»

ничего :D

И это «ничего» меня поставило в ступор сначала, но дело в том что я использую typedef и по какой то причине pahole игнорирует не именованные структуры, если исправить так

typedef struct collision{
  bool collided;
  float time;
  vec3 point;
  vec3 norm;
  int flags;
} collision;

То pahole выдаёт чёткие подробности

struct collision {
	_Bool                      collided;             /*     0     1 */

	/* XXX 3 bytes hole, try to pack */

	float                      time;                 /*     4     4 */
	vec3                       point;                /*     8    12 */
	vec3                       norm;                 /*    20    12 */
	int                        flags;                /*    32     4 */

	/* size: 36, cachelines: 1, members: 5 */
	/* sum members: 33, holes: 1, sum holes: 3 */
	/* last cacheline: 36 bytes */
};

Исправление на

typedef struct collision {
  vec3  point;
  vec3  norm;
  float time;
  int   flags;
  bool  collided;
} collision;

Убирает предупреждения/ошибки из gcc/clang, а pahole репортует что в целом всё впорядке

struct collision {
	vec3                       point;                /*     0    12 */
	vec3                       norm;                 /*    12    12 */
	float                      time;                 /*    24     4 */
	int                        flags;                /*    28     4 */
	_Bool                      collided;             /*    32     1 */

	/* size: 36, cachelines: 1, members: 5 */
	/* padding: 3 */
	/* last cacheline: 36 bytes */
};

Но, для полного счастья было бы хорошо заполнить 3 байта для выравнивания структур (массивы структур или дву/одно связные списки к примеру) в данном случае можно заменить bool на int или добавить заполнение char pad[3] если изменение типа структуры выливается в геморой ползания по коду или нарушает читабельность.

При окончательном изменении на

typedef struct collision {
  vec3  point;
  vec3  norm;
  float time;
  int   flags;
  bool  collided;
  char  __unused_struct_padding__[3];
} collision;

получаем репорт от pahole что всё ok


struct collision {
	vec3                       point;                /*     0    12 */
	vec3                       norm;                 /*    12    12 */
	float                      time;                 /*    24     4 */
	int                        flags;                /*    28     4 */
	_Bool                      collided;             /*    32     1 */
	char                       __unused_struct_padding__[3]; /*    33     3 */

	/* size: 36, cachelines: 1, members: 6 */
	/* last cacheline: 36 bytes */
};

Заполнять пустотой это конечно такое себе, но зато на будущее есть понимание что практически бесплатно можно будет в эту структуру засунуть ещё три флага например, таким образом использовать для дела в пустую в данном случае гоняемую память.

Ну вот как то так.

★★★★★

Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)

Паковать полезно при сериализации, чтобы место экономить. Минусы: процессор может работать медленнее с не выровненными данными.

ox55ff ★★★★★
()

Нарушается выравнивание, оттого код работает медленнее или вообще никак, в зависимости от архитектуры.

Нужно проверять на практике.

anonymous
()

Pragma pack вообще не про это. Ее основное назначение описание хардварно зависимых структур в памяти. Например запись в таблице страниц. Или заголовок tcp пакета.

zaz ★★★★
()

Где-то говорят что промахи кеша уменьшаются

Внесите Царя. Он покажет перформанс анскильным макакам.

ox55ff ★★★★★
()

Паковать лучше перестановкой порядка элементов, а не pragmapack’ом. Так ты сохранишь выравнивание.

i-rinat ★★★★★
()
Ответ на: комментарий от deep-purple

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

LINUX-ORG-RU ★★★★★
() автор топика

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

luntik2012
()
Ответ на: комментарий от i-rinat

Этим и занялся по началу, тоесть от меньших типов к большим, типа было


typedef struct {
  vec3 position;
  vec3 target;
  
  vec3 diffuse_color;
  vec3 specular_color;
  vec3 ambient_color;
  
  float power;
  float falloff;
  
  bool enabled;
  bool cast_shadows;
  
  int type;
  
  /* Shadow Mapping */
  vec3 shadow_color;
  int shadow_map_width;
  int shadow_map_height;
  
  /* Orthographic Shadow Mapping */
  bool orthographic;
  float ortho_width;
  float ortho_height;
  
  /* Projection Shadow Mapping */
  float fov;
  float aspect_ratio;
  
} light;

стало

typedef struct {

  bool enabled;
  bool cast_shadows;
  bool orthographic;

  int  type;
  int  shadow_map_width;
  int  shadow_map_height;

  float power;
  float falloff;
  float ortho_width;
  float ortho_height;  
  float fov;
  float aspect_ratio;

  vec3 position;
  vec3 target;
  vec3 diffuse_color;
  vec3 specular_color;
  vec3 ambient_color;
  vec3 shadow_color;

} light;

Ну, только минус в том что читабельность чуть хуже, не выглядит лучше, но к примеру /* Shadow Mapping */ блок уже размазан.

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

Обычно наоборот, бОльшие в начало.
И есть такая штука pahole, она тебе быстренько скажет где что плохо. Не обязательно будет переставлять все элементы структуры.

imul ★★★★★
()
Последнее исправление: imul (всего исправлений: 1)
Ответ на: комментарий от anonymous

Pahole умеет чуть больше, чем предупредить о нарушении выравнивания.

imul ★★★★★
()
Ответ на: комментарий от i-rinat

Спасибо большое, удобная и наглядная фиговина =) Пойду глядеть

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от imul

Обычно наоборот, бОльшие в начало.

Ой, да, ведь от перехода от меньшего к большему будет дырка, туплю.

И есть такая штука pahole, она тебе быстренько скажет где что плохо. Не обязательно будет переставлять все элементы структуры.

Ага, уже гляжу, автоматически перестраивать как я гляжу оно может, но там как я гляжу нюансы есть, оно выводит добротный лог, да, где что. А я уже ручками пройдусь и переставлю всё, чёбы всё было аккуратно по возможности красивенько =)

LINUX-ORG-RU ★★★★★
() автор топика

Пакуют в первую очередь для ipc в том числе между процессами собранными различными компиляторами под различные платформы. Что бы udp заголовок всегда оставался udp заголовком например. Ну и вообще при коммуникации с любым простым железом обычно есть чёткий протокол по смещениям каждого поля и по порядку байт для числовых полей. Это кейс про pack 1 когда нигде ничего не выравнивается.

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

Зависит от. В основном компилятор старается паковать так, что бы обращение к отдельным полям конкретного экземпляра структуры при должном выравнивании вызывало меньше миссов и лучше размещалось в памяти(в частности что бы если структура не помещается в одну страницу/кэшлинию/ещё какой нить блок никакое поле не оказалось разделено границей этого блока на 2 части(что замедлит обращение к этому полю минимум в 2 раза). Однако, если разработчик знает лучше это и правда может помочь снизить количество миссов.

В общем для ipc можно ставить pack1 практически бездумно, но если ты хочешь чего-то выиграть(или хотя бы не проиграть) в производительности надо понимать каким образом и какими порциями будет размещена структура в памяти.

pon4ik ★★★★★
()

Упаковка она не для экономии памяти (сильно просаживает производительность), она для чтения-записи файлов и для отправки-приёма сетевых пакетов. Потому что выравнивание архитектурно-специфично, а упакованные структуры одинаково выглядят на любой архитектуре (хотя для полной кросс-платформенности нужно ещё порядок байт в числах приводить к общему, но это легко).

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)

Короч. Если у тебя такой вопрос возник, то тебе оно точно не нужно. Когда оно тебя будет нужно - ты узнаешь. /thread

anonymous
()

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

Не понял такой связи кеша с конвеером. Отсутствие данных лишь стопорит его.

Вообще, зря не веришь. Я несколько раз оптимизировал размер структур (не только порядком, но и заменой bool flag на unsigned int flag : 1) и стабильно получал повышение производительности. Делал это, понятное дело, когда экземпляров структур много, иначе разница заметна не будет.

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

xaizek ★★★★★
()

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

man pahole

anonymous
()

плюсы

стала она меньше памяти занимать

да

минусы

Нестандарт. Работает в зависимости от настроения мозоли на левой задней пятке разработчика конпелятора.

Только вконец бакланутые дятлы используют структуры (вообще структуры, не только packed) для сериализации данных чтобы передать их через сеть или сохранить на диск. Совместимость зависящая от чужой пятки — зло.

Для ipc между форками одного процесса покатит, конпелятор-то гарантировано один и тот же.

anonymous
()

Да, кстати. У тебя почему-то флаги — в целом со знаком. А это значит, что бит 31 у тебя так и так свободный, даже если все остальные заняты. Иначе UB, крах и апокалипсис. Если поменяешь int на unsigned int, сможешь впихнуть collided в flags, и тогда у тебя структура влезет в 32 байта. А два экземпляра структуры, соответственно, в кеш-линию. Если выделять память выровненно, через posix_memalign(), можно добиться того, что ни один экземпляр структуры никогда не будет дёргать две линии.

Не думаю, что прям скорости до небес взлетят. Но ЧСВ поднимется, это точно.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Да, кстати. У тебя почему-то флаги — в целом со знаком.

Ну это ещё не у меня, а пока что у Даниэля, я сейчас глянул там вообще flags нигде не используется кроме в слиянии коллизий в наименьшие значения

collision collision_merge(collision c0, collision c1) {
  
  collision c;
  c.collided = c0.collided || c1.collided;
  c.time  = min(c0.time, c1.time);
  c.point = c0.time < c1.time ? c0.point : c1.point;
  c.norm  = c0.time < c1.time ? c0.norm  : c1.norm;
  c.flags = c0.time < c1.time ? c0.flags : c1.flags;
  
  return c;
}

Так что да можно спокойно привести в целочисленное или вовсе удалить. Короче там ещё не паханное поле =)

Но ЧСВ поднимется, это точно. Не, я чесать не люблю =) Но в целом спасибо, так то надо ещё в куче мест алокацию вообще убрать, хз, думать надо, а про posix_memalign вообще не знал, спасибо, звучит здорово.

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

Ток мой внутренний голос мне при этом говорит - «Ты придурок, вместо того что-бы на байты дрочить пилил бы игру, а не зарывался в движке». Частично я с ним согласен, поэтому пока что пройдусь по всем структурам приглажу их. А уж если будет время или прям необходимость прикоснусь к аллокаторам. =)

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от i-rinat

:D Я как бы хотел другую игру, ну да ладно тоже правда xD

LINUX-ORG-RU ★★★★★
() автор топика

пакуют при работе под разные архитектуры. ты не можешь заранее сказать, как будет работать/не работать выравнивание и какого размера типы данных на отдельно взятой машине/железяке. поэтому для работы в сети, для работы на разных платформах, для работы с железом используется pack.

з.ы. видела как-то, как три великовозрастных хипстера не могли победить упаковку в сетевом протоколе. бились они недели две. потом ко мне пришли: у нас байты неправильно передаются! потом ещё два дня не верили, что бывает порядок байт и упаковка (они в 30 с лишним лет узнали, что поля структуры выравниваются). потом мне пришлось им написать этот кусок для сети. я так и не поняла, дошло до них или нет.

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от Iron_Bug

Не ну не знать то это ладно, взял да узнал, а вот то что

потом ещё два дня не верили, что бывает порядок байт и упаковка два дня не верили

Я конечно сам тот ещё профан, но чёт улыбнуло =) Ну, ничего, теперь инфа о существовании разрядности вычислительных машин и всё остальное как следствие и них есть брусок рабочего кода на котором если захотеть то можно понять что да как. Ну люди разные бывают, может там им та хрень раз в жизни нужна была и решили налабать сами особо не вникая в суть и больше как бы им нахрен не нужно это ибо интересы другие, не смертельно =)

Хотя, поржать тоже иногда надо ))))))))))))))))))))))))))))))

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от Iron_Bug

три великовозрастных хипстера

потом мне пришлось им написать этот кусок для сети

После чего они допили свои авокадно-смородиновые смузи и укатили на своих Ламборгини. А ты так до сих пор и пишешь куски для сети.

i-rinat ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

серьёзно. они мне не верили и утверждали, что я несу какую-то чушь и «так не бывает». а я с 12 лет на ассемблере пишу. как не бывает-то? :) я тогда не стала с ними спорить, дала им таймаут на то, чтобы переварить информацию и просветиться в интернетах. потом пришли снова - за советом, как сделать.

это всё программизм высокого уровня. абстракции всякие. а как до дела доходит - так всё, кикоз :)

Iron_Bug ★★★★★
()
Ответ на: комментарий от i-rinat

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

да, ламборгини мне тоже не надо. я вообще не хочу покупать машину. мне такси хватает за глаза.

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 2)
Ответ на: комментарий от anonymous

нет. профессия водителя - самая низкооплачиваемая по статистике. мне удобнее покупать водителя, когда это нужно, чем заниматься этим самостоятельно и иметь весь гемор с содержанием, оплатой и обслуживанием автомобиля. тем более с нашими раздолбанными дорогами, грязюкой, гололёдом, колеёй, вечными пробками, оленями на дорогах, жадными гиббонами, кеглями, прыгающими под колёса и прочим маразмом. нафиг мне оно сдалось - тратить свои нервы на всё это безобразие?

причём езда на такси дешевле покупки и содержания любого приличного авто. так что покупка авто нужна только тем, у кого есть спиногрызы, тёщи и дачи. для остальных это типовое ненужно.

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 3)
Ответ на: комментарий от Iron_Bug

Когда ты уже уедешь в Соединенные Государства Америки? У тебя же английский свободный и интеллект выше среднего? Что ты делаешь в Пидорашке?

anonymous
()
Ответ на: комментарий от anonymous

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

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

Iron_Bug ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.