Коли програмування в CI виявило, що безцінне пакування конструкцій за допомогою GCC __attribute__((__packed__))
[...]
Оскільки ви згадуєте __attribute__((__packed__))
, я вважаю, що ваш намір полягає в тому, щоб усунути всі прокладки всередині struct
(зробити так, щоб кожен член мав 1-байтове вирівнювання).
Чи не існує стандарту для пакування структур, який працює у всіх компіляторах C?
... а відповідь - "ні". Прокладка і вирівнювання даних відносно структури (і суміжних масивів структур у стеку чи купу) існують з важливої причини. На багатьох машинах нестандартний доступ до пам'яті може призвести до значного покарання продуктивності (хоч і стає менш у порівнянні з новим обладнанням). У деяких рідкісних сценаріях неправильно узгоджений доступ до пам’яті призводить до помилки шини, яку неможливо відновити (може навіть збити всю операційну систему).
Оскільки стандарт C орієнтований на портативність, мало сенсу мати стандартний спосіб усунення всіх накладок у структурі та просто дозволяти довільні поля нерівні, оскільки це може потенційно ризикувати зробити код C непереносним.
Найбезпечніший і портативний спосіб виведення таких даних на зовнішнє джерело таким чином, що виключає всі заливки, - це серіалізація в / з потоків байтів замість того, щоб просто намагатися надсилати нерозроблений вміст пам'яті вашого structs
. Це також заважає вашій програмі зазнавати покарань за продуктивність поза цим контекстом серіалізації, а також дозволить вам вільно додавати нові поля в режим struct
без скидання та виблискування всього програмного забезпечення. Це також дасть вам трохи місця для подолання випадковості та подібних речей, якщо це колись стане проблемою.
Існує один спосіб усунення всіх заміток, не звертаючись до конкретних директив компілятора, хоча він застосовний лише у тому випадку, коли відносний порядок між полями не має значення. Дано щось подібне:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
... нам потрібна підкладка для вирівнювання доступу до пам'яті відносно адреси структури, що містить ці поля, наприклад:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... де .
вказує набивання. Кожен x
повинен відповідати 8-байтовій межі для продуктивності (а іноді навіть правильної поведінки).
Ви можете усунути прокладку портативно, використовуючи представлення SoA (структура масиву) на зразок такого (припустимо, нам потрібно 8 Foo
екземплярів):
struct Foos
{
double x[8];
char y[8];
};
Ми ефективно зруйнували конструкцію. У цьому випадку представлення пам'яті стає таким:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... і це:
01234567
yyyyyyyy
... немає більше накладних накладних витрат і без залучення нерівного доступу до пам'яті, оскільки ми більше не отримуємо доступ до цих полів як зміщення адреси структури, а натомість як зміщення базової адреси для того, що фактично є масивом.
Це також приносить бонус за швидкість для послідовного доступу в результаті зменшення кількості даних (більше нерелевантних прокладок у суміші, щоб уповільнити відповідну швидкість споживання даних машини), а також потенціал для компілятора векторизувати обробку дуже тривіально .
Мінус полягає в тому, що це ПДТА для кодування. Це також потенційно менш ефективно для випадкового доступу з більшим кроком між полями, де часто повторення AoS або AoSoA будуть робити краще. Але це один стандартний спосіб усунути прокладки і упакувати речі максимально щільно, не закручуючи вирівнювання всього.