디버그 빌드와 릴리스 빌드의 성능 차이


280

나는 보통 내 프로그램에서 디버그 구성 과 릴리스 구성 간 전환을 방해하지 않았 으며 프로그램이 실제로 고객 위치에 배포 된 경우에도 디버그 구성 을 사용하기로 결정했습니다 .

내가 아는 한, 수동으로 변경하지 않으면 이러한 구성 간의 유일한 차이점은 디버그DEBUG상수가 정의되어 있고 릴리스최적화 코드가 선택되어 있다는 것입니다.

그래서 내 질문은 실제로 두 가지입니다.

  1. 이 두 구성간에 성능 차이가 많이 있습니까? 여기에 성능에 큰 차이를 일으키는 특정 유형의 코드가 있습니까? 아니면 실제로 그렇게 중요하지 않습니까?

  2. 릴리스 구성에서 실패 할 수 있는 디버그 구성에서 제대로 실행되는 모든 유형의 코드가 있습니까 , 또는 디버그 구성 에서 테스트되고 올바르게 작동하는 코드가 릴리스 구성에서도 제대로 작동 하는지 확신 할 수 있습니다 .


답변:


511

C # 컴파일러 자체는 릴리스 빌드에서 방출 된 IL을 크게 변경하지 않습니다. 주목할 점은 더 이상 중괄호에 중단 점을 설정할 수있는 NOP opcode를 방출하지 않는다는 것입니다. 가장 큰 것은 JIT 컴파일러에 내장 된 최적화 프로그램입니다. 나는 그것이 다음과 같은 최적화를한다는 것을 알고있다.

  • 인라인 방법. 메소드 호출은 메소드의 코드 삽입으로 대체됩니다. 이것은 큰 것으로, 속성 접근자를 본질적으로 무료로 만듭니다.

  • CPU 레지스터 할당. 지역 변수와 메소드 인수는 스택 프레임에 다시 저장되지 않고 CPU 레지스터에 저장된 상태로 유지 될 수 있습니다. 이것은 최적화 된 코드 디버깅을 어렵게 만드는 데 주목할만한 큰 것입니다. 그리고 휘발성 키워드에 의미를 부여합니다 .

  • 배열 인덱스 검사 제거. 배열 작업시 중요한 최적화 (모든 .NET 컬렉션 클래스는 배열을 내부적으로 사용) JIT 컴파일러가 루프가 범위를 벗어난 배열을 색인화하지 않는지 확인할 수 있으면 색인 검사가 제거됩니다. 큰 것.

  • 언 롤링 루프. 본문에서 코드를 최대 4 번 반복하고 반복 횟수를 줄임으로써 작은 몸체가있는 루프가 개선됩니다. 지점 비용을 줄이고 프로세서의 슈퍼 스칼라 실행 옵션을 향상시킵니다.

  • 데드 코드 제거. if (false) {/ ... /} 와 같은 문장 은 완전히 제거됩니다. 이것은 일정한 접힘과 인라인으로 인해 발생할 수 있습니다. 다른 경우는 JIT 컴파일러가 코드에 부작용이 없음을 확인할 수있는 경우입니다. 이 최적화는 프로파일 링 코드를 매우 까다롭게 만듭니다.

  • 코드 게양. 루프의 영향을받지 않는 루프 내부의 코드는 루프 밖으로 이동할 수 있습니다. C 컴파일러의 최적화 프로그램은 호이스트 기회를 찾는 데 더 많은 시간을 할애합니다. 그러나 필요한 데이터 흐름 분석으로 인해 비용이 많이 드는 최적화이며 지터가 시간을 할애 할 수 없으므로 명백한 경우 만 호이스트합니다. .NET 프로그래머가 더 나은 소스 코드를 작성하고 호이스트하도록 강요합니다.

  • 일반적인 하위 표현 제거. x = y + 4; z = y + 4; z = x가된다; dest [ix + 1] = src [ix + 1]과 같은 문장에서 매우 흔합니다. 도우미 변수를 도입하지 않고 가독성을 위해 작성되었습니다. 가독성을 손상시킬 필요가 없습니다.

  • 일정한 접힘. x = 1 + 2; x = 3이되고; 이 간단한 예제는 컴파일러에 의해 일찍 발견되지만 다른 최적화가이를 가능하게하는 JIT 시간에 발생합니다.

  • 전파를 복사하십시오. x = a; y = x; y = a가된다; 이것은 레지스터 할당자가 더 나은 결정을 내리는 데 도움이됩니다. 작업 할 레지스터가 거의 없기 때문에 x86 지터에서 큰 문제입니다. 올바른 것을 선택하는 것은 성능에 중요합니다.

예를 들어 앱의 디버그 빌드를 프로파일 링하고 릴리스 빌드와 비교할 때 차이를 만들 수있는 매우 중요한 최적화입니다 . 코드가 중요한 경로에있을 때 실제로 중요합니다. 실제로 작성한 코드의 5 ~ 10 % 프로그램 성능에 영향을 미칩니다. JIT 옵티마이 저는 중요한 것이 무엇인지 미리 알기에 충분히 똑똑하지 않으며 모든 코드에 대해 "11로 돌리기"다이얼 만 적용 할 수 있습니다.

프로그램 실행 시간에 대한 이러한 최적화의 효과적인 결과는 종종 다른 곳에서 실행되는 코드의 영향을받습니다. 파일 읽기, dbase 쿼리 실행 등. JIT 최적화 프로그램이 완전히 보이지 않게합니다. 그래도 상관 없습니다 :)

JIT 최적화 프로그램은 수백만 번 테스트를 거쳤기 때문에 매우 안정적인 코드입니다. 릴리스 빌드 버전의 프로그램에 문제가있는 경우는 매우 드 rare니다. 그러나 발생합니다. x64와 x86 지터 모두 구조체에 문제가있었습니다. x86 지터는 부동 소수점 일관성에 문제가있어 부동 소수점 계산의 중간체가 메모리로 플러시 될 때 잘리지 않고 80 비트 정밀도로 FPU 레지스터에 유지 될 때 미묘하게 다른 결과를 생성합니다.


23
모든 컬렉션이 배열을 사용 한다고 생각 LinkedList<T>하지는 않습니다. 매우 자주 사용되지는 않지만 배열을 사용 하지 않습니다.
svick

CLR은 FPU를 53 비트 정밀도 (64 비트 와이드 더블과 일치)로 구성하므로 Float64 값에 대한 80 비트 확장 이중 계산이 없어야한다고 생각합니다. 그러나 Float32 계산은이 53 비트 정밀도로 계산 될 수 있으며 메모리에 저장 될 때만 잘립니다.
Govert

2
volatile키워드는 스택 프레임에 저장된 로컬 변수에 적용되지 않습니다. msdn.microsoft.com/en-us/library/x13ttww7.aspx 의 설명서에서 "휘발성 키워드는 클래스 또는 구조체의 필드에만 적용 할 수 있습니다. 로컬 변수는 일시적으로 선언 할 수 없습니다."
Kris Vandermotten 님이

8
겸손한 수정으로, 실제로 이와 관련하여 빌드 Debug와 의 차이점을 만드는 Release것은 일반적으로 켜져 Release있지만 꺼져 있는 "코드 최적화"확인란 입니다 Debug. 독자가 Visual Studio의 프로젝트 속성 페이지에있는 것보다 두 가지 빌드 구성간에 "마법적인"눈에 보이지 않는 차이점이 있다고 생각하지 않도록하기 위해서입니다.
chiccodoro

3
아마도 System.Diagnostics.Debug의 메소드 중 어느 것도 디버그 빌드에서 아무것도 수행하지 않는다고 언급 할 가치가 있습니다. 또한 변수는 너무 빨리 마무리되지 않습니다 ( stackoverflow.com/a/7165380/20553 ).
Martin Brown

23
  1. 예, 많은 성능 차이가 있으며 이는 실제로 코드 전체에 적용됩니다. 디버그는 성능 최적화를 거의하지 않으며 모드를 많이 해제합니다.

  2. DEBUG상수 에 의존하는 코드 만 릴리스 빌드에서 다르게 수행 될 수 있습니다. 그 외에도 아무런 문제가 없어야합니다.

DEBUG상수에 의존하는 프레임 워크 코드의 예 Debug.Assert()는 속성이 [Conditional("DEBUG)"]정의 된 메소드 입니다. 이는 DEBUG상수에 따라 달라지며 릴리스 빌드에는 포함되지 않습니다.


2
이것은 모두 사실이지만 차이를 측정 할 수 있습니까? 아니면 프로그램을 사용하는 동안 차이점을 알 수 있습니까? 물론 누구나 디버그 모드에서 소프트웨어를 출시하도록 장려하고 싶지는 않지만 성능 차이가 큰지 여부는 문제였습니다.
testalino

2
또한 디버그 버전은 원본 소스 코드와 릴리스 버전보다 훨씬 높은 상관 관계가 있습니다. 누군가가 실행 파일을 리버스 엔지니어링하려고한다고 생각하는 경우 (아마도) 디버그 버전을 배포하여 실행 파일을 더 쉽게 만들고 싶지는 않습니다.
jwheron

2
@testalino-글쎄, 요즘 어렵다. 프로세서는 사용자 조치로 인해 프로세스가 실제로 코드를 실행하기를 거의 기다리지 않는다는 것을 빨리 알고 있으므로 이는 상대적입니다. 그러나 실제로 긴 프로세스를 수행하는 경우 예를 알 수 있습니다. 다음 코드 예는 아래에 40 % 느리게 실행 DEBUG: AppDomain.CurrentDomain.GetAssemblies().Sum(p => p.GetTypes().Sum(p1 => p1.GetProperties().Length)).
Pieter van Ginkel

2
또한 asp.net릴리스 대신 디버그를 사용하는 경우 MicrosoftAjax.debug.js약 7k 줄 이있는 일부 스크립트가 페이지에 추가 될 수 있습니다 .
BrunoLM

13

이것은 응용 프로그램의 특성에 크게 좌우됩니다. 응용 프로그램이 UI가 많은 경우 최신 컴퓨터에 연결된 가장 느린 구성 요소가 사용자이므로 차이가 없을 것입니다. 일부 UI 애니메이션을 사용하는 경우 DEBUG 빌드에서 실행할 때 눈에 띄는 지연을 감지 할 수 있는지 테스트 할 수 있습니다.

그러나 계산이 많은 계산이 많은 경우에는 차이가 있음을 알 수 있습니다 (계산의 특성에 따라 @Pieter가 언급 한 것보다 40 % 높을 수 있음).

기본적으로 디자인 트레이드 오프입니다. DEBUG 빌드에서 릴리스하는 경우 사용자에게 문제가 발생하면 더 의미있는 역 추적을 얻을 수 있으며 훨씬 유연한 진단을 수행 할 수 있습니다. DEBUG 빌드를 릴리스하면 최적화 프로그램이 희미한 Heisenbugs를 생성하지 않아도 됩니다.


11
  • 필자의 경험은 중간 규모 이상의 응용 프로그램이 릴리스 빌드에서 눈에 띄게 반응한다는 것입니다. 응용 프로그램을 사용 해보고 느낌을 확인하십시오.

  • Release 빌드로 물릴 수있는 한 가지는 디버그 빌드 코드가 경쟁 조건 및 기타 스레딩 관련 버그를 억제 할 수 있다는 것입니다. 최적화 된 코드는 명령 순서가 변경 될 수 있으며 실행 속도가 빠르면 특정 경쟁 조건이 악화 될 수 있습니다.


9

프로덕션 환경에 .NET 디버그 빌드를 출시해서는 안됩니다. Edit-and-Continue를 지원하기위한 못생긴 코드 나 다른 것을 아는 사람이있을 수 있습니다. 내가 아는 한 이것은 C #이 아닌 VB에서만 발생하지만 (참고 : 원본 게시물에는 C # 태그가 지정되어 있음) 여전히 Microsoft가 디버그 빌드로 할 수 있다고 생각하는 것에 대해 일시 중지 할 이유가 있어야합니다. 실제로 .NET 4.0 이전에는 VB 코드가 Edit-and-Continue를 지원하기 위해 생성 한 이벤트가있는 개체의 인스턴스 수에 비례하여 메모리가 누출됩니다. (이것은 https://connect.microsoft.com/VisualStudio/feedback/details/481671/vb-classes-with-events-are-not-garbage-collected-when-debugging 에 따라 수정 된 것으로보고되었지만 생성 된 코드 불쾌한 것처럼 보이고 WeakReference객체를 만들고 정적 목록에 추가하는 동안 자물쇠 들고) 확실히 프로덕션 환경에서 이런 종류의 디버깅 지원을 원하지 않습니다!


디버그 빌드를 여러 번 릴리스했지만 문제가 발생하지 않았습니다. 유일한 차이점은 서버 측 응용 프로그램이 많은 사용자를 지원하는 웹 응용 프로그램이 아니라는 것입니다. 그러나 처리 부하가 매우 높은 서버 측 응용 프로그램입니다. 내 경험상 디버그와 릴리스의 차이점은 완전히 이론적으로 보입니다. 우리의 앱과 실질적인 차이를 본 적이 없습니다.
Sam Goldberg

5

내 경험에 따르면 릴리스 모드에서 나온 최악의 상황은 모호한 "릴리스 버그"입니다. IL (중급 언어)은 릴리스 모드에서 최적화되므로 디버그 모드에서 나타나지 않을 버그가있을 수 있습니다. 이 문제를 다루는 다른 SO 질문이 있습니다. 릴리스 버전의 버그가 디버그 모드에없는 일반적인 이유

이것은 간단한 콘솔 앱이 디버그 모드에서 완벽하게 잘 실행되지만 정확히 동일한 입력이 주어지면 릴리스 모드에서 오류가 발생하는 한두 번 나에게 일어났습니다. 이러한 버그는 릴리스 모드의 정의에 따라 아이러니하게 디버깅하기가 매우 어렵습니다.


다음은 릴리스 버그의 예를 제공하는 기사입니다. codeproject.com/KB/trace/ReleaseBug.aspx
Roly

배포 중에 릴리스 빌드가 실패하는 경우 오류를 억제하더라도 응용 프로그램을 디버그 설정으로 테스트하고 승인 한 경우 여전히 문제입니다.
Øyvind Bråthen

4

나는 1) 주로 구현에 달려 있다고 말한다. 일반적으로 그 차이는 그리 크지 않습니다. 나는 많은 측정을했고 종종 차이를 볼 수 없었습니다. 관리되지 않는 코드, 많은 거대한 배열 및 이와 유사한 것들을 사용하면 성능 차이가 약간 커지지 만 다른 세계 (C ++과 같은)는 아닙니다. 2) 일반적으로 릴리스 코드에서 적은 오류가 표시되고 (높은 공차) 스위치가 제대로 작동합니다.


1
IO 바인딩 된 코드의 경우 릴리스 빌드가 디버그보다 빠르지 않습니다.
Richard

0
    **Debug Mode:**
    Developer use debug mode for debugging the web application on live/local server. Debug mode allow developers to break the execution of program using interrupt 3 and step through the code. Debug mode has below features:
   1) Less optimized code
   2) Some additional instructions are added to enable the developer to set a breakpoint on every source code line.
   3) More memory is used by the source code at runtime.
   4) Scripts & images downloaded by webresource.axd are not cached.
   5) It has big size, and runs slower.

    **Release Mode:**
    Developer use release mode for final deployment of source code on live server. Release mode dlls contain optimized code and it is for customers. Release mode has below features:
   1) More optimized code
   2) Some additional instructions are removed and developer cant set a breakpoint on every source code line.
   3) Less memory is used by the source code at runtime.
   4) Scripts & images downloaded by webresource.axd are cached.
   5) It has small size, and runs fast.

2
릴리스 모드보다 목록의 첫 번째 요소가 올바르게 번호가 매겨지지 않은 것 같습니다. 또한 목록 내의 일부 요소가 복제됩니다. :)
지안 파올로
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.