Tại sao gcc lấp đầy toàn bộ mảng bằng số 0 thay vì chỉ 96 số nguyên còn lại? Các khởi tạo khác không là tất cả ở đầu mảng.
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
}
Cả MinGW8.1 và gcc9.2 đều tạo ra asm như thế này ( trình thám hiểm trình biên dịch Godbolt ).
# 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
(với SSE được bật, nó sẽ sao chép cả 4 trình khởi tạo với tải / lưu trữ Movdqa)
Tại sao GCC không làm lea edi, [esp+16]
và ghi nhớ (với rep stosd
) chỉ 96 yếu tố cuối cùng, giống như Clang không? Đây có phải là một tối ưu hóa bị bỏ lỡ, hoặc bằng cách nào đó hiệu quả hơn để làm theo cách này? (Clang thực sự gọi memset
thay vì nội tuyến rep stos
)
Lưu ý của biên tập viên: câu hỏi ban đầu có đầu ra trình biên dịch không được tối ưu hóa hoạt động theo cùng một cách, nhưng mã không hiệu quả tại -O0
không chứng minh được điều gì. Nhưng hóa ra việc tối ưu hóa này bị GCC bỏ lỡ ngay cả tại -O3
.
Truyền con trỏ đến a
một hàm không nội tuyến sẽ là một cách khác để buộc trình biên dịch thực hiện a[]
, nhưng trong mã 32 bit dẫn đến sự lộn xộn đáng kể của mã asm. (Stack args dẫn đến kết quả đẩy, được trộn lẫn với các cửa hàng vào ngăn xếp để khởi tạo mảng.)
Sử dụng volatile a[100]{1,2,3,4}
được GCC để tạo và sau đó sao chép mảng, đó là điên rồ. Thông thường volatile
là tốt để xem cách trình biên dịch khởi tạo các biến cục bộ hoặc đặt chúng ra trên ngăn xếp.
.rodata
... Tôi không thể tin rằng sao chép 400 byte nhanh hơn zeroing và đặt 8 mục.
-O3
( điều đó xảy ra ). godbolt.org/z/rh_TNF
missed-optimization
từ khóa.
a[0] = 0;
sau và sau đóa[0] = 1;
.