Quando la programmazione in CI ha trovato inestimabile impacchettare le strutture usando GCC __attribute__((__packed__))
[...]
Dato che dici __attribute__((__packed__))
, presumo che la tua intenzione sia quella di eliminare tutto il padding all'interno di un struct
(fare in modo che ogni membro abbia un allineamento di 1 byte).
Non esiste uno standard per l'imballaggio delle strutture che funziona in tutti i compilatori C?
... e la risposta è "no". Il riempimento e l'allineamento dei dati relativi a una struttura (e matrici contigue di strutture in pila o heap) esistono per un motivo importante. Su molte macchine, l'accesso alla memoria non allineato può comportare una riduzione delle prestazioni potenzialmente significativa (sebbene si riduca su un hardware più recente). In alcuni casi rari, l'accesso alla memoria disallineato porta a un errore del bus irrecuperabile (può persino causare l'arresto anomalo dell'intero sistema operativo).
Poiché lo standard C è incentrato sulla portabilità, non ha molto senso avere un modo standard per eliminare tutta l'imbottitura in una struttura e consentire solo che i campi arbitrari vengano disallineati, dal momento che ciò rischierebbe potenzialmente di rendere il codice C non portabile.
Il modo più sicuro e portatile per trasmettere tali dati a una fonte esterna in modo da eliminare tutto il riempimento è serializzare da / a flussi di byte invece di cercare di inviare il contenuto della memoria non elaborata structs
. Ciò impedisce anche al tuo programma di subire penali di prestazione al di fuori di questo contesto di serializzazione e ti consentirà inoltre di aggiungere liberamente nuovi campi a struct
senza buttare via e glitch l'intero software. Ti darà anche un po 'di spazio per affrontare l'endianità e cose del genere se mai dovesse diventare una preoccupazione.
Esiste un modo per eliminare tutto il riempimento senza raggiungere le direttive specifiche del compilatore, sebbene sia applicabile solo se l'ordine relativo tra i campi non ha importanza. Dato qualcosa del genere:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
... abbiamo bisogno del padding per l'accesso alla memoria allineato rispetto all'indirizzo della struttura contenente questi campi, in questo modo:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... dove .
indica imbottitura. Ciascuno x
deve allinearsi a un limite di 8 byte per le prestazioni (e talvolta anche il comportamento corretto).
Puoi eliminare il riempimento in modo portatile usando una rappresentazione SoA (struttura dell'array) in questo modo (supponiamo di aver bisogno di 8 Foo
istanze):
struct Foos
{
double x[8];
char y[8];
};
Abbiamo demolito efficacemente la struttura. In questo caso, la rappresentazione della memoria diventa così:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... e questo:
01234567
yyyyyyyy
... non più il sovraccarico di padding e senza comportare un accesso disallineato alla memoria poiché non stiamo più accedendo a questi campi di dati come offset di un indirizzo di struttura, ma invece come offset di un indirizzo di base per ciò che è effettivamente un array.
Ciò comporta anche il vantaggio di essere più veloce per l'accesso sequenziale a causa sia del minor consumo di dati (nessuna imbottitura più irrilevante nel mix per rallentare la velocità di consumo dei dati rilevanti della macchina) sia di un potenziale per il compilatore di vettorializzare l'elaborazione in modo molto banale .
Il rovescio della medaglia è che è un codice PITA da codificare. È anche potenzialmente meno efficiente per l'accesso casuale con il passo più grande tra i campi, dove spesso le ripetizioni AoS o AoSoA faranno meglio. Ma questo è un modo standard per eliminare l'imbottitura e imballare le cose nel modo più stretto possibile senza rovinare l'allineamento di tutto.