Mengapa gcc mengisi seluruh array dengan nol alih-alih hanya 96 bilangan bulat yang tersisa? Inisialisasi non-nol semua pada awal array.
void *sink;
void bar() {
int a[100]{1,2,3,4};
sink = a; // a escapes the function
asm("":::"memory"); // and compiler memory barrier
// forces the compiler to materialize a[] in memory instead of optimizing away
}
MinGW8.1 dan gcc9.2 keduanya membuat asm seperti ini ( Godbolt compiler explorer ).
# gcc9.2 -O3 -m32 -mno-sse
bar():
push edi # save call-preserved EDI which rep stos uses
xor eax, eax # eax=0
mov ecx, 100 # repeat-count = 100
sub esp, 400 # reserve 400 bytes on the stack
mov edi, esp # dst for rep stos
mov DWORD PTR sink, esp # sink = a
rep stosd # memset(a, 0, 400)
mov DWORD PTR [esp], 1 # then store the non-zero initializers
mov DWORD PTR [esp+4], 2 # over the zeroed part of the array
mov DWORD PTR [esp+8], 3
mov DWORD PTR [esp+12], 4
# memory barrier empty asm statement is here.
add esp, 400 # cleanup the stack
pop edi # and restore caller's EDI
ret
(dengan SSE diaktifkan, ia akan menyalin semua 4 inisialisasi dengan movdqa load / store)
Mengapa GCC tidak melakukan lea edi, [esp+16]
dan memset (dengan rep stosd
) hanya 96 elemen terakhir, seperti yang dilakukan Clang? Apakah ini optimasi yang tidak terjawab, atau entah bagaimana lebih efisien untuk melakukannya dengan cara ini? (Dentang sebenarnya menelepon memset
bukannya inlining rep stos
)
Catatan editor: pertanyaan awalnya adalah keluaran kompiler yang tidak dioptimalkan yang bekerja dengan cara yang sama, tetapi kode yang tidak efisien di -O0
tidak membuktikan apa-apa. Tetapi ternyata pengoptimalan ini dilewatkan oleh GCC bahkan pada -O3
.
Melewati pointer ke a
fungsi non-inline akan menjadi cara lain untuk memaksa kompiler terwujud a[]
, tetapi dalam kode 32-bit yang mengarah pada kekacauan signifikan asm. (Stack args menghasilkan push, yang akan bercampur dengan toko ke stack untuk init array.)
Menggunakan volatile a[100]{1,2,3,4}
get GCC untuk membuat dan kemudian menyalin array, yang gila. Biasanya volatile
baik untuk melihat bagaimana kompiler init variabel lokal atau meletakkannya di stack.
.rodata
... Saya tidak percaya menyalin 400 byte lebih cepat daripada mem-nolkan dan mengatur 8 item.
-O3
(yang terjadi). godbolt.org/z/rh_TNF
missed-optimization
kata kunci.
a[0] = 0;
dan kemudiana[0] = 1;
.