programing

C++ 대 C로 컴파일 시 GCC 코드 생성의 큰 차이

newnotes 2023. 9. 12. 20:43
반응형

C++ 대 C로 컴파일 시 GCC 코드 생성의 큰 차이

저는 사용 가능한 다양한 SIMD 확장(MMX, SSE, AVX)에 대해 더 자세히 알아보려 x86-64 어셈블리를 가지고 약간 놀았습니다.

C나 C++ 구조가 어떻게 GCC에 의해 기계 코드로 변환되는지 확인하기 위해 훌륭한 도구인 컴파일러 탐색기를 사용해 왔습니다.

'플레이 세션' 중 하나에서 저는 GCC가 정수 배열의 간단한 런타임 초기화를 어떻게 최적화할 수 있는지 알고 싶었습니다.이 경우 저는 부호 없는 정수 2048개 배열에 0부터 2047까지의 숫자를 쓰려고 했습니다.

코드는 다음과 같습니다.

unsigned int buffer[2048];

void setup()
{
  for (unsigned int i = 0; i < 2048; ++i)
  {
    buffer[i] = i;
  }
}

최적화 및 AVX-512 지침을 활성화하는 경우-O3 -mavx512f -mtune=intelGCC 6.3은 정말 영리한 코드를 생성합니다 :)

setup():
        mov     eax, OFFSET FLAT:buffer
        mov     edx, OFFSET FLAT:buffer+8192
        vmovdqa64       zmm0, ZMMWORD PTR .LC0[rip]
        vmovdqa64       zmm1, ZMMWORD PTR .LC1[rip]
.L2:
        vmovdqa64       ZMMWORD PTR [rax], zmm0
        add     rax, 64
        cmp     rdx, rax
        vpaddd  zmm0, zmm0, zmm1
        jne     .L2
        ret
buffer:
        .zero   8192
.LC0:
        .long   0
        .long   1
        .long   2
        .long   3
        .long   4
        .long   5
        .long   6
        .long   7
        .long   8
        .long   9
        .long   10
        .long   11
        .long   12
        .long   13
        .long   14
        .long   15
.LC1:
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16

그러나 GCC C-compiler를 사용하여 flags를 추가하여 동일한 코드를 컴파일하면 어떤 결과가 발생하는지 테스트해 보았을 때-x c나 정말 놀랐었어요.

저는 동일하지는 않지만 비슷한 결과를 기대했지만 C 컴파일러는 훨씬 더 복잡하고 아마도 훨씬 느린 기계 코드를 생성하는 것 같습니다.결과 어셈블리의 크기가 너무 커서 전체를 붙여넣을 수 없지만, 이 링크를 따라 godbolt.org 에서 볼 수 있습니다.

생성된 코드의 일부분인 58번부터 83번까지는 아래와 같습니다.

.L2:
        vpbroadcastd    zmm0, r8d
        lea     rsi, buffer[0+rcx*4]
        vmovdqa64       zmm1, ZMMWORD PTR .LC1[rip]
        vpaddd  zmm0, zmm0, ZMMWORD PTR .LC0[rip]
        xor     ecx, ecx
.L4:
        add     ecx, 1
        add     rsi, 64
        vmovdqa64       ZMMWORD PTR [rsi-64], zmm0
        cmp     ecx, edi
        vpaddd  zmm0, zmm0, zmm1
        jb      .L4
        sub     edx, r10d
        cmp     r9d, r10d
        lea     eax, [r8+r10]
        je      .L1
        mov     ecx, eax
        cmp     edx, 1
        mov     DWORD PTR buffer[0+rcx*4], eax
        lea     ecx, [rax+1]
        je      .L1
        mov     esi, ecx
        cmp     edx, 2
        mov     DWORD PTR buffer[0+rsi*4], ecx
        lea     ecx, [rax+2]

보시다시피 이 코드는 복잡한 움직임과 점프가 많고 일반적으로 단순한 배열 초기화를 수행하는 매우 복잡한 방식처럼 느껴집니다.

생성된 코드에 왜 이렇게 큰 차이가 나죠?

일반적으로 GCC C++ 컴파일러는 C와 C++에서 모두 유효한 코드를 최적화하는 데 있어 C 컴파일러와 비교할 때 더 우수합니까?

추가 코드는 사용된 명령어가 잘못 정렬된 것을 처리하기 위한 것입니다.vmovdqa64, 64바이트 정렬이 필요합니다.

내 테스트 결과 표준이 그렇지 않더라도 gcc는 C 모드일 때 다른 모듈의 정의를 무시할 수 있습니다.이 정의는 기본 정렬 요구 사항(4바이트)만 준수할 수 있으므로 컴파일러는 더 큰 정렬에 의존할 수 없습니다.기술적으로, GCC는 다음과 같이.comm외부 정의가 정규 기호를 사용하는 동안, 이 잠정적 정의에 대한 어셈블리 지시..data부분. 하는 동안 이링크하는 기호다음 우선합니다 기호보다 동안 the 우선합니다 over보다 우선합니다..comm

할 을 할 을 하도록 프로그램을 변경할 경우 참고합니다.extern unsigned int buffer[2048];그러면 C++ 버전에서도 코드가 추가됩니다.반대로 성공하는 것.static unsigned int buffer[2048];는 C 버전을 최적화된 버전으로 바꿉니다.

언급URL : https://stackoverflow.com/questions/41292971/big-differences-in-gcc-code-generation-when-compiling-as-c-vs-c

반응형