Lorsque la programmation dans CI a trouvé inestimable de pack structs à l'aide de GCC __attribute__((__packed__))
[...]
Puisque vous le mentionnez __attribute__((__packed__))
, je suppose que votre intention est d'éliminer tout remplissage dans un struct
(faire en sorte que chaque membre ait un alignement sur 1 octet).
N'y a-t-il pas de norme pour l'emballage des structures qui fonctionne dans tous les compilateurs C?
... Et la réponse est non". Le remplissage et l'alignement des données par rapport à une structure (et les tableaux contigus de structures en pile ou en tas) existent pour une raison importante. Sur de nombreuses machines, l'accès à la mémoire non aligné peut entraîner une pénalité de performance potentiellement importante (bien qu'elle devienne moindre sur certains matériels plus récents). Dans certains cas rares, un accès à la mémoire mal aligné entraîne une erreur de bus irrécupérable (peut même planter l'ensemble du système d'exploitation).
Étant donné que la norme C est axée sur la portabilité, il est peu logique de disposer d'un moyen standard pour éliminer tout le remplissage dans une structure et simplement permettre un désalignement des champs arbitraires, car cela risquerait potentiellement de rendre le code C non portable.
Le moyen le plus sûr et le plus portable de sortir ces données vers une source externe d'une manière qui élimine tout remplissage est de sérialiser vers / depuis les flux d'octets au lieu d'essayer simplement d'envoyer le contenu de la mémoire brute de votre structs
. Cela empêche également votre programme de subir des pénalités de performances en dehors de ce contexte de sérialisation, et vous permettra également d'ajouter librement de nouveaux champs à un struct
sans supprimer et perturber le logiciel entier. Cela vous donnera également de la place pour lutter contre l'endianité et des choses comme ça si cela devient un problème.
Il existe un moyen d'éliminer tout remplissage sans atteindre les directives spécifiques au compilateur, bien qu'il ne soit applicable que si l'ordre relatif entre les champs n'a pas d'importance. Étant donné quelque chose comme ça:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
... nous avons besoin du remplissage pour un accès mémoire aligné par rapport à l'adresse de la structure contenant ces champs, comme ceci:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... où .
indique un rembourrage. Chacun x
doit s'aligner sur une limite de 8 octets pour les performances (et parfois même un comportement correct).
Vous pouvez éliminer le remplissage de manière portable en utilisant une représentation SoA (structure de tableau) comme ceci (supposons que nous ayons besoin de 8 Foo
instances):
struct Foos
{
double x[8];
char y[8];
};
Nous avons effectivement démoli la structure. Dans ce cas, la représentation de la mémoire devient comme ceci:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... et ça:
01234567
yyyyyyyy
... plus de surcharge de remplissage, et sans impliquer un accès à la mémoire mal aligné puisque nous n'accédons plus à ces champs de données comme décalage d'une adresse de structure, mais plutôt comme décalage d'une adresse de base pour ce qui est effectivement un tableau.
Cela présente également l'avantage d'être plus rapide pour l'accès séquentiel en raison de la réduction de la consommation de données (plus de remplissage non pertinent dans le mélange pour ralentir le taux de consommation de données pertinent de la machine) et également du potentiel pour le compilateur de vectoriser le traitement de manière très triviale. .
L'inconvénient est que c'est un PITA à coder. Il est également potentiellement moins efficace pour l'accès aléatoire avec une plus grande foulée entre les champs, où souvent les représentants AoS ou AoSoA feront mieux. Mais c'est une façon standard d'éliminer le rembourrage et d'emballer les choses aussi étroitement que possible sans visser avec l'alignement de tout.