ทำไม gcc เติมทั้งอาร์เรย์ด้วยศูนย์แทนที่จะเป็นจำนวนเต็ม 96 เท่านั้นที่เหลือ? initializers ที่ไม่ใช่ศูนย์ทั้งหมดอยู่ที่จุดเริ่มต้นของอาร์เรย์
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 และ gcc9.2 สร้าง asm เช่นนี้ ( 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
(เมื่อเปิดใช้งาน SSE จะเป็นการคัดลอก initializers ทั้ง 4 ตัวที่มีโหลด / เก็บ movdqa)
เหตุใด GCC จึงไม่ทำlea edi, [esp+16]
และ memset (พร้อมrep stosd
) เฉพาะองค์ประกอบ 96 รายการสุดท้ายอย่างที่ Clang ทำ นี่เป็นการเพิ่มประสิทธิภาพที่ไม่ได้รับหรือเป็นวิธีที่มีประสิทธิภาพมากกว่านี้หรือไม่ (เสียงดังกังวานเรียกจริง ๆmemset
แทนที่จะเป็นอินไลน์rep stos
)
หมายเหตุจากบรรณาธิการ: คำถามแรกเริ่มมีการคอมไพล์เลอร์เอาท์พุทที่ไม่ได้รับการปรับให้เหมาะสมซึ่งทำงานในลักษณะเดียวกัน แต่โค้ดที่ไม่มีประสิทธิภาพที่-O0
ไม่ได้พิสูจน์อะไรเลย แต่ปรากฎว่าการเพิ่มประสิทธิภาพนี้จะพลาดโดย GCC -O3
แม้ที่
การส่งตัวชี้ไปa
ยังฟังก์ชันที่ไม่ใช่แบบอินไลน์จะเป็นอีกวิธีหนึ่งในการบังคับคอมไพเลอร์ให้เป็นจริงa[]
แต่ในรหัส 32 บิตที่นำไปสู่ความยุ่งเหยิงที่สำคัญของ asm (สแต็ก args ส่งผลให้เกิดการพุชซึ่งจะผสมกับร้านค้าในสแต็คเพื่อเริ่มต้นอาร์เรย์)
การใช้volatile a[100]{1,2,3,4}
ทำให้ GCC สร้างและคัดลอกอาเรย์ซึ่งไม่ได้ผล โดยปกติแล้วvolatile
ดีสำหรับการดูว่าคอมไพเลอร์เริ่มต้นตัวแปรท้องถิ่นหรือวางมันลงบนสแต็กได้อย่างไร
.rodata
... ฉันไม่อยากจะเชื่อเลยว่าการคัดลอก 400 ไบต์นั้นเร็วกว่า zeroing และตั้งค่า 8 รายการ
-O3
(ซึ่งเป็น) godbolt.org/z/rh_TNF
missed-optimization
คำสำคัญ
a[0] = 0;
a[0] = 1;