왜 GCC가 배열의 초기화를 초기화하여 0이 아닌 요소를 포함하여 모든 것을 먼저 0으로 채우는가?

왜 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컴파일러가 로컬 변수를 초기화하거나 스택에 배치하는 방법을 보는 것이 좋습니다.



답변

이론적으로 초기화는 다음과 같습니다.

int a[100] = {
  [3] = 1,
  [5] = 42,
  [88] = 1,
};

따라서 전체 메모리 블록을 먼저 제로화 한 다음 개별 값을 설정하는 것이 캐시 및 최적화 측면에서 더 효과적 일 수 있습니다.

다음에 따라 동작이 변경 될 수 있습니다.

  • 대상 아키텍처
  • 대상 OS
  • 배열 길이
  • 초기화 비율 (명시 적으로 초기화 된 값 / 길이)
  • 초기화 된 값의 위치

물론 배열의 시작 부분에서 초기화가 압축되어 최적화가 쉽지 않습니다.

따라서 gcc가 가장 일반적인 접근 방식 인 것 같습니다. 최적화가 누락 된 것 같습니다.


답변