C/C++ 프로그램의 디버그 모드에서 종종 최적화가 꺼지는 이유는 무엇입니까?

StackOverflow https://stackoverflow.com/questions/69250

  •  09-06-2019
  •  | 
  •  

문제

대부분의 C 또는 C++ 환경에는 "디버그" 모드와 "릴리스" 모드 컴파일이 있습니다.
둘 사이의 차이점을 살펴보면 디버그 모드가 디버그 기호(보통 많은 컴파일러에서 -g 옵션)를 추가하지만 대부분의 최적화도 비활성화된다는 것을 알 수 있습니다.
"릴리스" 모드에서는 일반적으로 모든 종류의 최적화가 켜져 있습니다.
왜 차이점이 있나요?

도움이 되었습니까?

해결책

최적화가 없으면 코드의 흐름이 선형입니다.5번째 줄에 있고 한 단계씩 진행 중이라면 6번째 줄로 이동합니다.최적화를 켜면 명령 재정렬, 루프 풀기 및 모든 종류의 최적화를 얻을 수 있습니다.
예를 들어:


void foo() {
1:  int i;
2:  for(i = 0; i < 2; )
3:    i++;
4:  return;

이 예에서는 최적화 없이 코드를 한 단계씩 실행하고 1, 2, 3, 2, 3, 2, 4 행을 칠 수 있습니다.

최적화를 켜면 다음과 같은 실행 경로를 얻을 수 있습니다.2, 3, 3, 4 또는 심지어 4개만!(결국 함수는 아무것도 하지 않습니다...)

결론적으로, 최적화가 활성화된 코드 디버깅은 엄청난 고통이 될 수 있습니다!특히 큰 기능을 가지고 있는 경우에는 더욱 그렇습니다.

최적화를 켜면 코드가 변경된다는 점에 유의하세요!특정 환경(안전이 중요한 시스템)에서는 이는 허용되지 않으며 디버깅되는 코드는 배송된 코드여야 합니다.이 경우 최적화를 사용하여 디버그해야 합니다.

최적화된 코드와 최적화되지 않은 코드는 "기능적으로" 동일해야 하지만 특정 상황에서는 동작이 변경됩니다.
다음은 간단한 예입니다.

    int* ptr = 0xdeadbeef;  // some address to memory-mapped I/O device
    *ptr = 0;   // setup hardware device
    while(*ptr == 1) {    // loop until hardware device is done
       // do something
    }

최적화를 끄면 이는 간단하며 무엇을 기대해야 할지 어느 정도 알 수 있습니다.그러나 최적화를 켜면 다음과 같은 몇 가지 일이 발생할 수 있습니다.

  • 컴파일러는 while 블록을 최적화할 수 있습니다(0으로 초기화하므로 1이 될 수 없습니다).
  • 메모리에 액세스하는 대신 포인터 액세스가 레지스터->I/O 업데이트 없음으로 이동될 수 있습니다.
  • 메모리 액세스가 캐시될 수 있습니다(반드시 컴파일러 최적화와 관련된 것은 아님).

이 모든 경우에 동작은 크게 다르며 잘못된 가능성이 높습니다.

다른 팁

디버그와 릴리스의 또 다른 중요한 차이점은 로컬 변수가 저장되는 방식입니다.개념적으로 지역 변수는 함수 스택 프레임에 저장 공간을 할당합니다.컴파일러가 생성한 기호 파일은 디버거에게 스택 프레임에 있는 변수의 오프셋을 알려주므로 디버거가 이를 표시할 수 있습니다.디버거는 이를 수행하기 위해 메모리 위치를 엿봅니다.

그러나 이는 지역 변수가 변경될 때마다 해당 소스 라인에 대해 생성된 코드가 값을 스택의 올바른 위치에 다시 써야 함을 의미합니다.이는 메모리 오버헤드로 인해 매우 비효율적입니다.

릴리스 빌드에서 컴파일러는 함수의 일부에 대한 레지스터에 지역 변수를 할당할 수 있습니다.어떤 경우에는 스택 저장소를 전혀 할당하지 않을 수도 있습니다(머신에 레지스터가 많을수록 이 작업이 더 쉬워집니다).

그러나 디버거는 레지스터가 코드의 특정 지점에 대한 로컬 변수에 어떻게 매핑되는지 알지 못하므로(이 정보가 포함된 기호 형식은 알지 못함) 이를 정확하게 표시할 수 없습니다. 그것을 찾으려면 어디로 가야할지 모르겠습니다.

또 다른 최적화는 함수 인라인입니다.최적화된 빌드에서 컴파일러는 함수가 충분히 작기 때문에 foo()에 대한 호출을 foo가 사용되는 모든 곳에서 실제 코드로 대체할 수 있습니다.그러나 foo()에 중단점을 설정하려고 하면 디버거는 foo()에 대한 명령의 주소를 알고 싶어하며 더 이상 이에 대한 간단한 대답이 없습니다. 수천 개의 foo( ) 코드 바이트가 프로그램 전체에 퍼집니다.디버그 빌드는 중단점을 넣을 위치가 있음을 보장합니다.

코드 최적화는 의미를 유지하면서 코드의 런타임 성능을 향상시키는 자동화된 프로세스입니다.이 프로세스는 표현식이나 함수 평가를 완료하는 데 필요하지 않지만 디버깅할 때 유용할 수 있는 중간 결과를 제거할 수 있습니다.마찬가지로, 최적화는 소스 코드에 나타나는 것과 약간 다른 순서로 상황이 발생할 수 있도록 명백한 제어 흐름을 변경할 수 있습니다.이는 불필요하거나 중복되는 계산을 건너뛰기 위해 수행됩니다.이러한 코드 재조정은 소스 코드 줄 번호와 개체 코드 주소 간의 매핑을 망쳐 디버거가 작성한 제어 흐름을 따르기가 어렵게 만들 수 있습니다.

최적화되지 않은 모드에서 디버깅하면 최적화 프로그램이 항목을 제거하거나 순서를 변경하지 않고도 작성한 모든 내용을 작성한 대로 볼 수 있습니다.

프로그램이 올바르게 작동하는 것에 만족하면 최적화를 활성화하여 향상된 성능을 얻을 수 있습니다.요즘 최적화 프로그램은 꽤 신뢰할 만하지만 프로그램이 최적화된 모드와 최적화되지 않은 모드 모두에서 동일하게(성능을 고려하지 않고 기능적 관점에서) 실행되는지 확인하기 위해 좋은 품질의 테스트 모음을 구축하는 것이 좋습니다.

디버그 버전이 디버그될 것으로 예상됩니다!중단점 설정, 변수 관찰 중 한 단계씩 실행, 스택 추적 및 디버거(IDE 또는 기타)에서 수행하는 기타 모든 작업은 비어 있지 않고 주석이 아닌 소스 코드의 모든 줄이 일부 기계어 명령과 일치하는 경우 의미가 있습니다.

대부분의 최적화는 기계 코드의 순서를 뒤죽박죽으로 만듭니다.루프 풀기(Loop unrolling)가 좋은 예입니다.공통 하위 표현식을 루프에서 꺼낼 수 있습니다.최적화를 켜면 가장 단순한 수준이라도 기계어 수준에서는 존재하지 않는 줄에 중단점을 설정하려고 할 수 있습니다.때때로 지역 변수가 CPU 레지스터에 보관되어 있거나 심지어 최적화되어 있지 않기 때문에 모니터링할 수 없습니다!

소스 수준이 아닌 명령어 수준에서 디버깅하는 경우 최적화되지 않은 명령어를 소스에 다시 매핑하는 것이 훨씬 더 쉽습니다.또한 컴파일러는 때때로 최적화 프로그램에 버그가 있습니다.

Microsoft의 Windows 사업부에서는 모든 릴리스 바이너리가 디버깅 기호와 전체 최적화를 통해 구축됩니다.기호는 별도의 PDB 파일에 저장되며 코드 성능에 영향을 주지 않습니다.제품과 함께 배송되지는 않지만 대부분 매장에서 구매 가능합니다. 마이크로소프트 기호 서버.

최적화와 관련된 또 다른 문제는 인라인 함수입니다. 이는 항상 한 단계씩 처리한다는 점에서 그렇습니다.

디버깅과 최적화가 함께 활성화된 GCC를 사용하면 무엇을 기대해야 할지 모른다면 코드가 오작동하고 동일한 명령문을 여러 번 다시 실행한다고 생각할 것입니다. 제 동료 몇 명이 이런 일을 겪었습니다.또한 최적화를 통해 GCC에서 제공하는 디버깅 정보는 실제로 가능한 것보다 품질이 떨어지는 경향이 있습니다.

그러나 Java와 같은 가상 머신에서 호스팅되는 언어에서는 최적화와 디버깅이 공존할 수 있습니다. 디버깅 중에도 네이티브 코드에 대한 JIT 컴파일이 계속되고 디버깅된 메서드의 코드만 최적화되지 않은 버전으로 투명하게 변환됩니다.

사용된 최적화 프로그램에 버그가 있거나 코드 자체에 버그가 있고 부분적으로 정의되지 않은 의미에 의존하지 않는 한 최적화는 코드의 동작을 변경해서는 안 된다는 점을 강조하고 싶습니다.후자는 다중 스레드 프로그래밍이나 인라인 어셈블리도 사용될 때 더 일반적입니다.

디버깅 기호가 있는 코드는 더 크며 이는 더 많은 캐시 누락을 의미할 수 있습니다.속도가 느려지는 것은 서버 소프트웨어에 문제가 될 수 있습니다.

적어도 Linux에서는(그리고 Windows가 달라야 할 이유가 없습니다) 디버그 정보는 바이너리의 별도 섹션에 패키지되어 있으며 일반 실행 중에 로드되지 않습니다.디버깅에 사용하기 위해 다른 파일로 분할할 수 있습니다.또한 일부 컴파일러(Gcc 포함, Microsoft C 컴파일러 포함)에서는 디버깅 정보와 최적화를 함께 활성화할 수 있습니다.그렇지 않으면 분명히 코드가 느려질 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top