CI programlama zaman GCCs kullanarak yapıları paketi için çok değerli bulduk __attribute__((__packed__))
[...]
Bahsettiğinizden beri __attribute__((__packed__))
, niyetinizin bir içindeki tüm dolguları ortadan kaldırmak olduğunu varsayıyorum struct
(her üyenin 1 baytlık bir hizalamaya sahip olmasını sağlayın).
Tüm C derleyicilerinde çalışan paketleme yapıları için bir standart yok mu?
... Ve cevap hayır". Bir yapıya (ve yığın veya yığındaki yapıların bitişik dizileri) göre dolgu ve veri hizalama önemli bir nedenden ötürü mevcuttur. Birçok makinede, hizalanmamış bellek erişimi potansiyel olarak önemli bir performans cezasına yol açabilir (ancak bazı yeni donanımlarda daha az olur). Bazı nadir durumlarda, yanlış hizalanmış bellek erişimi, kurtarılamayan bir veri yolu hatasına yol açar (hatta tüm işletim sistemini kilitleyebilir).
C standardı taşınabilirliğe odaklandığından, bir yapıdaki tüm dolguları ortadan kaldırmak ve sadece keyfi alanların yanlış hizalanmasına izin vermek için standart bir yol olması pek mantıklı değildir, çünkü bunu yapmak C kodunu taşınabilir hale getirme riskini doğurur.
Bu tür verileri tüm dolguyu ortadan kaldıracak şekilde harici bir kaynağa çıkarmanın en güvenli ve en taşınabilir yolu, yalnızca ham bellek içeriğini göndermeye çalışmak yerine bayt akışlarına / dizisinden serileştirmektir structs
. Bu ayrıca programınızın bu serileştirme bağlamının dışında performans cezalarına maruz kalmasını önler ve ayrıca struct
tüm yazılımı atmadan ve hata yapmadan yeni alanlara özgürce yeni alanlar eklemenizi sağlar . Endianlık ve bunun gibi şeyler ile başa çıkmak için biraz endişe duyuyorsanız size de yer açacaktır.
Derleyiciye özgü direktiflere ulaşmadan tüm dolguları ortadan kaldırmanın bir yolu vardır, ancak yalnızca alanlar arasındaki göreceli sıralama önemli değilse uygulanabilir. Böyle bir şey verildiğinde:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
... bu alanları içeren yapının adresine göre hizalanmış bellek erişimi için dolguya ihtiyacımız var, şöyle:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... .
dolguyu gösterir. Her x
biri performans için 8 baytlık bir sınıra (ve hatta bazen doğru davranışa) hizalanmalıdır.
Bunun gibi bir SoA (dizi yapısı) gösterimi kullanarak dolguyu taşınabilir bir şekilde ortadan kaldırabilirsiniz (8 Foo
örneğe ihtiyacımız olduğunu varsayalım ):
struct Foos
{
double x[8];
char y[8];
};
Yapıyı etkili bir şekilde yıktık. Bu durumda, bellek temsili şu şekilde olur:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... ve bu:
01234567
yyyyyyyy
... artık doldurma yükü yok ve bu veri alanlarına artık bir yapı adresinin ofseti olarak değil, etkili bir şekilde bir dizi için temel adresin ofseti olarak eriştiğimizden, yanlış hizalanmış bellek erişimi içermeden.
Bu aynı zamanda, hem tüketilecek daha az verinin (makinenin ilgili veri tüketim oranını yavaşlatmak için karışımda daha fazla alakasız dolgu olmaması) hem de derleyicinin işlemi çok önemsiz bir şekilde vektörelleştirmesinin bir sonucu olarak sıralı erişim için daha hızlı olma bonusu taşır. .
Dezavantajı kodlamak için bir PITA olmasıdır. Ayrıca, genellikle AoS veya AoSoA temsilcilerinin daha iyi olacağı alanlar arasındaki daha büyük adımlarla rastgele erişim için potansiyel olarak daha az verimlidir. Ancak bu, yastığı ortadan kaldırmanın ve her şeyin hizalanmasıyla vidalanmadan işleri mümkün olduğunca sıkı bir şekilde paketlemenin standart bir yoludur.