При программировании в CI было признано неоценимым упаковать структуры с использованием GCC __attribute__((__packed__))
[...]
Поскольку вы упоминаете __attribute__((__packed__))
, я предполагаю, что вы намерены устранить все отступы внутри struct
(сделать так, чтобы каждый элемент имел выравнивание в 1 байт).
Нет ли стандарта для структур упаковки, который работает во всех компиляторах Си?
... И ответ нет". Заполнение и выравнивание данных относительно структуры (и смежных массивов структур в стеке или куче) существуют по важной причине. На многих компьютерах доступ к памяти без выравнивания может привести к потенциально значительному снижению производительности (хотя на некоторых более новых аппаратных средствах его станет меньше). В некоторых редких случаях неправильное выравнивание доступа к памяти приводит к ошибке шины, которая не может быть исправлена (может даже привести к сбою всей операционной системы).
Поскольку стандарт 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
... не нужно больше накладных расходов и без вовлечения несогласованного доступа к памяти, поскольку мы больше не обращаемся к этим полям данных как к смещению структурного адреса, а как к смещению базового адреса для того, что фактически является массивом.
Это также дает преимущество более быстрого последовательного доступа благодаря меньшему потреблению данных (больше не нужно неуместного заполнения в миксе для замедления релевантной скорости использования данных машиной), а также способности компилятора очень просто векторизовать обработку ,
Недостатком является то, что это код PITA. Это также потенциально менее эффективно для произвольного доступа с большим шагом между полями, где часто повторения AoS или AoSoA будут лучше. Но это один из стандартных способов устранить набивку и упаковать вещи настолько плотно, насколько это возможно, не прибегая к выравниванию всего.