Quando a programação em CI achou inestimável empacotar estruturas usando GCCs __attribute__((__packed__))
[...]
Como você mencionou __attribute__((__packed__))
, presumo que sua intenção é eliminar todos os preenchimentos dentro de um struct
(faça com que cada membro tenha um alinhamento de 1 byte).
Não existe um padrão para empacotar estruturas que funcionem em todos os compiladores C?
... E a resposta é não". O preenchimento e o alinhamento de dados em relação a uma estrutura (e matrizes contíguas de estruturas na pilha ou pilha) existem por um motivo importante. Em muitas máquinas, o acesso desalinhado à memória pode levar a uma penalidade de desempenho potencialmente significativa (embora diminua em alguns hardwares mais novos). Em alguns casos raros, o acesso desalinhado à memória leva a um erro de barramento irrecuperável (pode até travar todo o sistema operacional).
Como o padrão C é focado na portabilidade, faz pouco sentido ter uma maneira padrão de eliminar todo o preenchimento em uma estrutura e permitir apenas que campos arbitrários sejam desalinhados, pois isso pode potencialmente arriscar tornar o código C não portátil.
A maneira mais segura e portátil de enviar esses dados para uma fonte externa de uma maneira que elimine todo o preenchimento é serializar de / para fluxos de bytes, em vez de apenas tentar enviar o conteúdo da memória não processada do seu structs
. Isso também evita que seu programa sofra penalidades de desempenho fora desse contexto de serialização e também permite adicionar livremente novos campos a um struct
sem desperdiçar e danificar todo o software. Também lhe dará espaço para lidar com endianness e coisas assim, se isso se tornar uma preocupação.
Existe uma maneira de eliminar todo o preenchimento sem alcançar diretivas específicas do compilador, embora isso seja aplicável apenas se a ordem relativa entre os campos não for importante. Dado algo parecido com isto:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
... precisamos do preenchimento para acesso alinhado à memória em relação ao endereço da estrutura que contém esses campos, da seguinte forma:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... onde .
indica preenchimento. Todos x
devem se alinhar a um limite de 8 bytes para desempenho (e às vezes até comportamento correto).
Você pode eliminar o preenchimento de maneira portátil usando uma representação SoA (estrutura da matriz) da seguinte forma (vamos supor que precisamos de 8 Foo
instâncias):
struct Foos
{
double x[8];
char y[8];
};
Nós efetivamente demolimos a estrutura. Nesse caso, a representação da memória fica assim:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... e isto:
01234567
yyyyyyyy
... não há mais sobrecarga de preenchimento e sem envolver acesso desalinhado à memória, pois não estamos mais acessando esses campos de dados como um deslocamento de um endereço de estrutura, mas como um deslocamento de um endereço de base para o que é efetivamente uma matriz.
Isso também traz o bônus de ser mais rápido no acesso seqüencial como resultado de menos dados consumidos (preenchimento irrelevante na mistura para diminuir a taxa relevante de consumo de dados da máquina) e também um potencial para o compilador vetorizar o processamento de maneira muito trivial .
A desvantagem é que é uma PITA codificar. Também é potencialmente menos eficiente para acesso aleatório com o passo mais amplo entre os campos, onde frequentemente os representantes de AoS ou AoSoA se saem melhor. Mas essa é uma maneira padrão de eliminar o preenchimento e embalar as coisas o mais firmemente possível, sem estragar o alinhamento de tudo.