consoledevФорум

К вопросу о выравнивании структур

#0
12:02, 26 мая 2006

Сегодня утром была интересная лекцыя. Привожу слегка почищенный лог.
=======================================================
[11:02] <dDIMA> Потому что например на песи народ обленился вконец и не знает страшного слова align
[11:03] * kas низнает
[11:03] <dDIMA> kas, скажи мне. Сколько будет sizeof() структуры следующего вида:
[11:04] <dDIMA> char c1; double f1; short c2;
[11:04] <kas> ну, если прагма пак(1), то так и будет 11
[11:04] <dDIMA> На целевой платформе sizeof(double)=8, sizeof(short)=2
[11:04] <dDIMA> прагмы нету. Default align = 8
[11:05] * dDIMA нилюбит прагма пак, ибо это сильно медленнее при промахе по кратности адреса
[11:05] <kas> ну, тада пусть будет 16
[11:05] <dDIMA> нифига
[11:05] <kas> чо, 24 шоли?
[11:06] * kas проверяет
[11:06] <dDIMA> ну поехали. Смещение поля с1 - очевидно, 0.
[11:06] <dDIMA> Сколько будет смещение поля f1?
[11:06] <kas> я бы сказал 1
[11:06] <dDIMA> double должен быть выровнен на свой sizeof()
[11:07] <dDIMA> поэтому его смещение будет 8
[11:09] <dDIMA> Поэтому после с1 и f1 структура уже занимает 16 байт
[11:10] <dDIMA> Потом идет с2, оно располагается со смещением 16 и 17, и общий размер структуры будет 18 байт
[11:10] <dDIMA> А дальше начинается самое страшное
[11:10] <dDIMA> Потому что компилятор думает: "а что если какой-то программер рикнет завести массив структур"
[11:11] <dDIMA> Это что-же получится? адрес поля f1 во втором элементе структуры станет не кратен 8?
[11:11] <dDIMA> Ни фига. Не позволю! И быстренько добивает структуру байтами до размера в 24
[11:13] <dDIMA> Так что примитивной перестановкой f1 в начало структуры мы можем уменьшить ее sizeof() в полтора раза!
[11:13] <kas> ща дима, 10 минут :)
[11:14] <dDIMA> можешь еще адреса полей проинспектировать и подсчитать реальные смещения полей
[11:14] <dDIMA> А если адрес нашей переменной double не кратен 8, то процессор встает раком.
[11:14] <kas> тогда нипанятно почему ссе не взлетает
[11:14] <kas> без мемманагера
[11:15] <kas> если он всё выравнивает на алигн заданый
[11:15] <kas> ну, для ссе надо выравнивание на 16
[11:15] <dDIMA> Для VC заданный алигн - 8
[11:15] <kas> ну, можно и 16 задатьже
[11:15] <kas> только не спасает
[11:15] <dDIMA> Наверное можно
[11:15] <dDIMA> Это означает, что структуры и типы, большие по размеру чем 8, будут алайницца максимум на 8.
[11:16] <dDIMA> А вообще интеловский процессор слишком умный
[11:16] <dDIMA> Если переменная типа double не выровнена на границу в 8 байт, все работает
[11:16] <dDIMA> Только процессор считывает по шине 2 адреса и компонует число уже у себя в мозгах
[11:17] <dDIMA> Уважающие себя процессоры при такой наглости предпочитают умирать с позором
[11:18] <dDIMA> То есть если ты задаешь aligh = 1, то все действительно стыкуецца вполтную друг к другу. Все поля
[11:18] <dDIMA> Но если ты сделаешь aligh по умолчанию и правильно распределишь поля в структурах, у тебя скорость доступа к полям возрастет вдвое

#1
12:24, 26 мая 2006

Что лучше делать в таком случае:

typedef struct
{
emath::mat4  matWorld;                // 16 bytes
emath::mat4  matRelative;             // 16 bytes
IMesh             *pMesh;                   // 4 bytes
IMaterial         *pMaterial;              // 4 bytes
short    nParentID;                         // -1 means no parent
short    nFirstChild;                        // -1 means no children
short    nChildCount;                     // -1 means no children
short    nbDoTransform;                // 0 means there are no relative transform
} t_submesh;

правка: tabs

#2
12:29, 26 мая 2006

Padawan
>Что лучше делать в таком случае:
> [cut]
Тут вроде все оптимально :)

#3
12:36, 26 мая 2006

Димка
Да. Но есть дополнение. Когда бежим по трансформациям, хочеца локальность их. При том жутко хочется, ибо vfpu. Вот индексы поэтому я бы отдельно делал. Не делал бы также иерархии для простых мешей, анимацию отдельно бы делал.

#4
13:48, 26 мая 2006

neteraser
Да, все правильно. В контексте оптимизации доступа к памяти: если сначала идет пакетная работа с matWorld, а потом пакетная работа с nbDoTransform (где t_submesh - это массив или, извиняюсь за выражение, std::vector) - лучше разнести на 2 структуры.
Сначала обрабатывать массив с matWorld, потом - массив с nbDoTransform.

#5
14:49, 26 мая 2006

еще бы было интересно попрофайлить на pad с выравниванием на 64 байта.

#6
23:39, 26 мая 2006

KVaks
Долго соображал, потом до меня дошло. Компилятор CW тебе не даст сделать pad на 64 байта, так как стандартный аллокатор выдает тебе адреса, кратные только 16 байтам. Соответственно, структуры с таким выравниванием надо или только создавать в стеке (там будет правильно), либо принудительно пользоваться для их обработки вызовов memalign().
Даже если удастся изнасиловать компилятор и заставить его это делать, потери от "рваного" распределения памяти (как внутри самих структур, так и внутри memory manager) будут уже слишком велики. ИМХО.

#7
0:53, 27 мая 2006

Димка
Ась??

Update: ну полюбому, а кто-то пользуется malloc?
я - нет, у меня custom new по типу stack alloc c маркерами.
там принудительно задается align.

p.s. а на вызов malloc у меня все падает. почему - не знаю. желания разбираться не должно быть :)

#8
9:52, 27 мая 2006

Димка

в случае статик __attribute__((aligned(64))) ?:)
в случае динамик конечно же memalign

для особых извращенцев :) :
  vifDma = new(memalign(64,sizeof(wglGfxDma)))wglGfxDma(SCE_DMA_VIF1,VIF1_BUFFER_SIZE);
  vifDma->m_DmacHandlerId = AddDmacHandler(DMAC_VIF1,DmaInterruptHandler,0);

:)

#9
12:03, 27 мая 2006

neteraser
Вот почему-то всегда, когда я сравниваю malloc с new, меня перестают понимать :)
А у них тем не менее общий heap, и память они выдают из него, выровненную на 16 байт.
Так что даже если написать у структуры (или у поля структуры) aligned(64), а потом распределить ее по new, все накроется.
То есть надо писать либо custom allocator, либо напрямую пользоваться memalign(64).

А общий аллокатор new переопределять на 64-байта выравнивания - очень нерациональная трата оперативной памяти.

#10
12:18, 27 мая 2006

Димка
> А общий аллокатор new переопределять на 64-байта выравнивания - очень нерациональная трата оперативной памяти.
А не надо такое. Надо new (user, 64) byte [64]; user - map, 64 - align. Т.е. всегда указывать и следить.

consoledevФорум

Тема в архиве.