[IDE] 비주얼 스튜디오 Release, Debug 모드의 차이점

    Visual Studio 프로젝트에서 빌드를 하는 방법에는 릴리스(Release)와 디버그(Debug) 방식이 있습니다. 하지만 정확한 차이점은 모르시는 분이 많더군요. 대부분 Debug 버전은 디버깅용으로 Release 버전은 최종 배포용으로 빌드하는 것과 Relase 방식이 Debug 방식보다 빠르다 이 정도로만 알고 있지 이 두 가지의 정확한 차이점은 대부분 알지 못하는 듯하여 두 모드의 핵심적인 차이점 몇 가지를 이 포스팅에서 한번 정리해보고자 합니다.

     

     

    Release에서는 코드 최적화를 하고 Debug에서는 하지 않는다.

    프로젝트 속성의 최적화로 들어가 보시면 Release는 위와 같이 최대 최적화로 되어 있는 반면 Debug는 사용 안 함으로 되어있는 것을 확인하실 수 있습니다. 여기서 코드 최적화란 무엇일까요? 코드 최적화는 고급 프로그래밍 언어(C언어나 C++)를 컴파일하는 과정에서 컴파일러가 판단하여 최적화를 진행할 수 있는 부분은 최적화를 한다는 뜻입니다.

     

    #include<stdio.h>
    int main() {
        int a = 0;
        a++;
        printf("%d",a);
    }
    

    예를 들어 위의 코드를 Debug모드와 Release모드에서 각각 컴파일한다고 가정해 보겠습니다.

     

    먼저 Debug모드로 위의 코드를 컴파일 후에 생성된 어셈블리어입니다. 코드에서 의도한 대로 a라는 가상공간에 0을 할당해주고 add 1을 해준 뒤 출력을 해주는 어셈블리어로 변환된 것을 보실 수 있습니다. 비주얼 스튜디오에서 컴파일 된 어셈블리어는 디스어셈블리 창에서 확인하실 수 있는데 이 방법을 모르시는 분은 아래 글을 참고 부탁드립니다.

    [IDE] 비주얼 스튜디오 어셈블리코드 확인하기 (DisAssembly)

     

    하지만 위의 코드를 Release모드에서 컴파일하면 위처럼 결과가 나오게 됩니다. 공간 할당 없이 그저 1을 냅다 출력하는 어셈블리어로 바뀌었습니다.

     printf("1"); 

    Release모드에서 컴파일하면 이 한 줄로 위의 코드 내용을 줄여버린 것입니다. 결과는 당연히 같습니다. a = 0을 선언하고 ++을 해준 뒤 a를 출력하는 것이나 그냥 1을 출력하는 것이나 동일하니 말입니다. 하지만 컴파일된 어셈블리어의 라인 수는 획기적으로 줄었습니다. Release모드에서 컴파일을 하면 코드 최적화가 되어 속도가 빨라진다는 말이 바로 이것을 의미합니다. 하지만 이런 식으로 코드 최적화를 하게 되면 소스 코드와 생성된 명령 간의 관계가 복잡해지므로 중단점이 제대로 안 찍히는 등의 문제가 발생하여 디버깅이 복잡해진다는 단점은 있습니다.

     

    Debug모드에서는 컴파일 시 코드의 안정성을 위해 여러 가지 장치를 추가한다.

    왼쪽은 Release 모드에서의 메모리 값이고 오른쪽은 Debug 모드에서의 메모리값입니다. 변수로 메모리 공간을 할당하고 그 값을 초기화시켜주기 전까지는 쓰레기 값이 들어가 있습니다. 이러한 쓰레기값이 문제를 일으킬만한 소지도 있겠죠. 그래서 Debug에서는 이 문제를 방지하기 위해 cc로 메모리 값을 밀어버립니다. Release모드의 경우 좌측 그림과 같이 쓰레기 값이 들어가 있는 반면 Debug에서는 우측 그림과 같이 쓰레기 값들을 전부 cc라는 값으로 초기화를 시켜준것을 보실 수 있습니다.

     

    이것 이외에도 Stack의 위 아래 공간을 여유공간을 둬서 널널하게 크기를 잡는다던지 하는 여러가지 장치가 마련되어 있습니다. 사용하고 있는 메모리들의 간격을 바짝 붙여서 사용하지 않는다는 뜻입니다. 또한 Debug모드는 컴파일 시 각종 Debug정보들을 넣어 디버깅이 편하게 만들어줍니다. 종합하자면 Release모드는 최적화 Debug모드는 안정성을 중시했다. 이렇게 정의할 수 있을 것 같습니다.  

     

    생성되는 파일의 크기가 다르다.

    위의 여러가지 장치들로 인해 Release로 컴파일한 것이 용량이 더 적은 것을 확인하실 수 있습니다. 이렇듯 릴리스 구성에 완전히 코드가 최적화되고 또한 위와 같이 사용하는 컴파일러 옵션에 따라 .pdb 파일이 생성될 수 있습니다. .pdb 파일을 만들면 나중에 릴리스 버전을 디버그해야 하는 경우 매우 유용할 수 있습니다. 

     

    사용되는 런타임 라이브러리가 다르다.

    위에서 확인하셨듯 Release모드와 Debug의 컴파일 목적이 다르기 때문에 위와 같이 사용하는 런타임 라이브러리가 서로 다릅니다.

     

    증분 링크 사용 여부가 다르다.

    디버그 모드의 경우 빌드를 생성할 때, 링커는 바이너리를 가능한 한 빠르게 링크하기 위해 패딩비트를 사용합니다. 따라서 어떤 함수가 변경되면 링커는 각 비트를 이동시켜서 변경된 부분만 삽입하는 형태가 아니라 그냥 전체를 오버라이드 해 버립니다. 이때 변경된 함수는 기존에 남아있던 공간보다 커질 수 있기 때문에 이 경우에는 링커가 해당 바이너리를 다른 곳으로 옮겨야 하는데, 만약 새로운 곳으로 이동된 함수를 호출할때 실질적인 함수 주소를 호출한다면, 함수가 이동될 때 마다 새로운 링크 때문에 링커는 모든 CALL 명령을 조사해서 새로운 주소로 갱신해야 합니다. 이때 사용되는 것이 증분 링크입니다. 함수들의 주소들을 테이블 형태로 구성하여 줌으로써 변경한 함수를 호출하는 부분의 코드를 재빌드할 필요 없이 함수 주소 테이블만 재 빌드 해주는 방식으로 빌드 타임을 줄일 수 있습니다. 또한 증분링크의 사용함으로써 디버그 모드에서 코드를 수정하면 디버그 창에서 계속해서 반영되게끔 만들 수 있습니다.

     

    맺음말

    어떤 코드의 경우에는 Debug모드에서는 정상적으로 작동하는데 Release모드에서는 정상적으로 작동하지 않는 코드들이 있습니다. 좋은 코드는 물론 두 모드에서 정상적으로 작동이 잘 되어야겠죠. 그렇기에 Debug로 작업을 진행하다가도 중간중간 Release모드로도 꼭 테스트를 진행하여야 합니다. 아니면 애초에 Release로 개발을 하던가요. 어떤 경우는 Release모드로 컴파일 한 코드가 정상적으로 작동하지 않아 최악의 경우에는 그냥 Debug모드로 컴파일한 것을 최종 배포에 사용하는 경우도 종종 있다고 합니다.

    댓글(0)

    Designed by JB FACTORY