왜 gcc는 나머지 96 개의 정수 대신에 전체 배열을 0으로 채우는가? 0이 아닌 이니셜 라이저는 모두 배열의 시작 부분에 있습니다.
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 컴파일러 탐색기 ).
# 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를 사용하면 movdqa로드 / 저장과 함께 4 개의 이니셜 라이저를 모두 복사합니다)
왜 GCC가 Clang처럼 마지막 96 개 요소 lea edi, [esp+16]
만하고 memset을 사용 rep stosd
하지 않습니까? 이것이 누락 된 최적화입니까, 아니면이 방법으로 더 효율적입니까? (Clang은 실제로 memset
인라인 대신 호출 rep stos
)
편집자 주 :이 질문에는 원래 최적화되지 않은 컴파일러 출력이 있었지만 같은 방식으로 작동했지만 비효율적 인 코드는 -O0
아무 것도 증명하지 못했습니다. 그러나이 최적화는 에서조차도 GCC에 의해 누락되었습니다 -O3
.
a
인라인이 아닌 함수에 대한 포인터를 전달하는 것은 컴파일러가 구체화하는 또 다른 방법 a[]
이지만 32 비트 코드에서 asm의 심각한 혼란을 초래합니다. 스택 인수로 인해 푸시가 발생하여 스택과 상점이 혼합되어 배열을 초기화합니다.
사용 volatile a[100]{1,2,3,4}
GCC는 다음 생성 및 도착 복사 미친 배열을. 일반적으로 volatile
컴파일러가 로컬 변수를 초기화하거나 스택에 배치하는 방법을 보는 것이 좋습니다.
.rodata
... 400 바이트를 복사하는 것이 0을 설정하고 8 항목을 설정하는 것보다 빠릅니다.
-O3
. godbolt.org/z/rh_TNF
missed-optimization
입니다. 키워드로 GCC의 버그 질라에보고하십시오 .
a[0] = 0;
하고a[0] = 1;
.