Apakah variabel stack diselaraskan dengan GCC __attribute __ ((aligned (x)))?

88

Saya memiliki kode berikut:

Dan saya memiliki output berikut:

Mengapa alamatnya a[0]bukan kelipatan 0x1000?

Apa sebenarnya yang __attribute__((aligned(x)))dilakukannya? Saya salah paham dengan penjelasan ini ?

Saya menggunakan gcc 4.1.2.

cojocar
sumber

Jawaban:

98

Saya percaya masalahnya adalah bahwa array Anda ada di tumpukan, dan kompiler Anda terlalu tua untuk mendukung variabel tumpukan yang terlalu selaras. GCC 4.6 dan yang lebih baru memperbaiki bug itu .

C11 / C ++ 11 alignas(64) float a[4];Hanya Berfungsi untuk semua kekuatan dari 2 keselarasan.
Begitu juga GNU C __attribute__((aligned(x)))saat Anda menggunakannya.

(Di C11, #include <stdalign.h>untuk #define alignas _Alignas: cppref ).


Namun dalam kasus perataan yang sangat besar, ke batas halaman 4k, Anda mungkin tidak menginginkannya di tumpukan.

Karena penunjuk tumpukan bisa berupa apa saja saat fungsi dimulai, tidak ada cara untuk menyelaraskan array tanpa mengalokasikan lebih banyak dari yang Anda butuhkan dan menyesuaikannya. (Kompiler akan and rsp, -4096atau setara dan tidak menggunakan salah satu dari 0 hingga 4088 byte yang dialokasikan; bercabang pada apakah ruang itu cukup besar atau tidak akan dimungkinkan tetapi tidak dilakukan karena perataan besar jauh lebih besar daripada ukuran array atau penduduk setempat lainnya bukan kasus normal.)

Jika Anda memindahkan array dari fungsi dan menjadi variabel global, itu akan berfungsi. Hal lain yang dapat Anda lakukan adalah menyimpannya sebagai variabel lokal (yang merupakan hal yang sangat bagus), tetapi buatlah static. Ini akan mencegahnya disimpan di tumpukan. Berhati-hatilah karena kedua cara ini tidak aman untuk thread atau rekursi-aman, karena hanya akan ada satu salinan larik.

Dengan kode ini:

Saya mengerti ini:

itulah yang diharapkan. Dengan kode asli Anda, saya hanya mendapatkan nilai acak seperti yang Anda lakukan.

Zifre
sumber
11
+1 jawaban yang benar. Solusi alternatifnya adalah membuat array lokal menjadi statis. Penjajaran pada tumpukan selalu menjadi masalah dan yang terbaik adalah membiasakan diri menghindarinya.
Dan Olson
Oh ya, saya tidak berpikir untuk membuatnya statis. Itu ide yang bagus karena mencegah nama yang bertabrakan. Saya akan mengedit jawaban saya.
Zifre
3
Perhatikan bahwa menjadikannya statis juga membuatnya tidak reentrant dan tidak aman untuk thread.
ArchaeaSoftware
3
Juga gcc 4.6+ menangani ini dengan benar bahkan pada stack.
textshell
1
Jawaban ini dulu benar, tetapi sekarang tidak. gcc setua 4.6, mungkin lebih lama, tahu cara menyelaraskan penunjuk tumpukan untuk mengimplementasikan C11 / C ++ 11 dengan benar alignas(64)atau apa pun pada objek dengan penyimpanan otomatis. Dan tentu saja GNU C__attribute((aligned((64)))
Peter Cordes
41

Ada bug di gcc yang menyebabkan atribut yang diselaraskan tidak berfungsi dengan variabel stack. Tampaknya sudah diperbaiki dengan tambalan yang ditautkan di bawah. Tautan di bawah ini juga berisi sedikit diskusi untuk masalah tersebut.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660

Saya telah mencoba kode Anda di atas dengan dua versi gcc yang berbeda: 4.1.2 dari kotak RedHat 5.7, dan gagal serupa dengan masalah Anda (array lokal sama sekali tidak selaras pada batas 0x1000 byte). Saya kemudian mencoba kode Anda dengan gcc 4.4.6 di RedHat 6.3, dan itu bekerja dengan sempurna (array lokal diselaraskan). Orang-orang Myth TV memiliki masalah serupa (yang tampaknya diperbaiki oleh gcc patch di atas):

http://code.mythtv.org/trac/ticket/6535

Bagaimanapun, sepertinya Anda menemukan bug di gcc, yang tampaknya telah diperbaiki di versi yang lebih baru.

rts1
sumber
3
Menurut bug terkait, gcc 4.6 adalah rilis pertama dengan masalah ini telah diperbaiki sepenuhnya untuk semua arsitektur.
textshell
Selain itu, kode assembly yang dihasilkan oleh gcc untuk membuat variabel yang diselaraskan pada stack sangat buruk dan tidak dioptimalkan. Jadi, apakah masuk akal untuk mengalokasikan variabel yang diselaraskan di stack daripada memanggil memalign()?
Jérôme Pouiller
13

GCC terbaru (diuji dengan 4.5.2-8ubuntu4) tampaknya berfungsi seperti yang diharapkan dengan larik yang disejajarkan dengan benar.

Saya mendapat:

Kasus Caleb
sumber
Ini sedikit mengejutkan, mengingat array dialokasikan di tumpukan - apakah itu berarti tumpukan sekarang penuh dengan lubang?
ysap
Atau tumpukannya sejajar 16-byte.
user7116
9

Penjajaran tidak efektif untuk semua jenis. Anda harus mempertimbangkan menggunakan struktur untuk melihat atribut beraksi:

Dan kemudian, Anda akan membaca:

Itulah yang Anda harapkan.

Edit: Didorong oleh @yzap dan mengikuti komentar @Caleb Case, masalah awal adalah karena versi GCC hanya . Saya telah memeriksa GCC 3.4.6 vs GCC 4.4.1 dengan kode sumber pemohon:

Sekarang jelas bahwa versi GCC yang lebih lama (sebelum 4.4.1) menunjukkan patologi penyelarasan.

Catatan 1: Kode yang saya usulkan tidak menjawab pertanyaan yang saya pahami sebagai "menyelaraskan setiap bidang larik".

Catatan 2: Membawa non-statis a [] ke dalam main () dan mengkompilasi dengan GCC 3.4.6 memutus arahan penyelarasan dari array struct tetapi menjaga jarak 0x1000 antar struct ... masih buruk! (lihat jawaban @zifre untuk solusi)

levif
sumber
2
Seperti yang dijawab oleh zifre, ini bukan tipenya, tetapi fakta bahwa Anda membuatnya statis dalam versi Anda.
ysap
@ysap, versi GCC dan definisi global-lah yang membuatnya berfungsi. Terima kasih atas komentar Anda! Saya mengedit jawaban untuk memperbaikinya. :)
levif