Ketika pemrograman dalam CI telah menemukan itu sangat berharga untuk mengemas struct menggunakan GCCs __attribute__((__packed__))
[...]
Karena Anda menyebutkan __attribute__((__packed__))
, saya menganggap niat Anda adalah untuk menghilangkan semua padding dalam a struct
(membuat setiap anggota memiliki keselarasan 1-byte).
Apakah tidak ada standar untuk packing struct yang berfungsi di semua kompiler C?
... Dan jawabannya adalah tidak". Padding dan penyelarasan data relatif terhadap sebuah struct (dan array berdekatan dari struct dalam stack atau heap) ada karena alasan penting. Pada banyak mesin, akses memori yang tidak selaras dapat menyebabkan potensi penalti kinerja yang signifikan (meskipun menjadi kurang pada beberapa perangkat keras yang lebih baru). Dalam beberapa skenario kasus yang jarang terjadi, akses memori yang tidak selaras menyebabkan kesalahan bus yang tidak dapat dipulihkan (bahkan dapat merusak seluruh sistem operasi).
Karena standar C difokuskan pada portabilitas, tidak masuk akal untuk memiliki cara standar untuk menghilangkan semua lapisan dalam struktur dan hanya membiarkan bidang yang sewenang-wenang diselaraskan, karena dengan melakukan hal itu berpotensi berisiko membuat kode C non-portabel.
Cara teraman dan paling portabel untuk mengeluarkan data seperti itu ke sumber eksternal dengan cara yang menghilangkan semua lapisan adalah dengan membuat serial ke / dari byte stream alih-alih hanya mencoba mengirim isi memori mentah Anda structs
. Itu juga mencegah program Anda dari menderita hukuman kinerja di luar konteks serialisasi ini, dan juga akan memungkinkan Anda untuk menambahkan bidang baru ke bebas struct
tanpa membuang dan merusak seluruh perangkat lunak. Ini juga akan memberi Anda ruang untuk mengatasi endianness dan hal-hal seperti itu jika itu menjadi masalah.
Ada satu cara untuk menghilangkan semua padding tanpa mencapai arahan khusus compiler, meskipun itu hanya berlaku jika urutan relatif antara bidang tidak masalah. Diberikan sesuatu seperti ini:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
... kita membutuhkan padding untuk akses memori yang disejajarkan relatif ke alamat struktur yang berisi bidang-bidang ini, seperti:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... di mana .
menunjukkan padding. Setiap orang x
harus menyelaraskan ke batas 8-byte untuk kinerja (dan kadang-kadang bahkan memperbaiki perilaku).
Anda dapat menghilangkan padding dengan cara portabel dengan menggunakan representasi SoA (structure of array) seperti itu (mari kita asumsikan kita membutuhkan 8 Foo
instance):
struct Foos
{
double x[8];
char y[8];
};
Kami telah secara efektif menghancurkan struktur. Dalam hal ini, representasi memori menjadi seperti ini:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... dan ini:
01234567
yyyyyyyy
... tidak ada lagi padding overhead, dan tanpa melibatkan akses memori yang tidak selaras karena kita tidak lagi mengakses bidang data ini sebagai penyeimbang alamat struktur, melainkan sebagai penyeimbang alamat dasar untuk apa yang secara efektif sebuah array.
Ini juga membawa bonus menjadi lebih cepat untuk akses berurutan sebagai akibat dari lebih sedikit data untuk dikonsumsi (tidak ada padding yang tidak relevan dalam campuran untuk memperlambat laju konsumsi data yang relevan dengan mesin) dan juga potensi bagi kompiler untuk melakukan vectorisasi pemrosesan dengan sangat sepele. .
The downside adalah bahwa itu adalah PITA ke kode. Ini juga berpotensi kurang efisien untuk akses acak dengan langkah lebih besar di antara bidang, di mana sering repetisi AoS atau AoSoA akan melakukan lebih baik. Tapi itu satu cara standar untuk menghilangkan bantalan dan mengemas barang sekencang mungkin tanpa mengacaukan segalanya.