보통 C++로 코드 작성 후 결과물을 실행 파일로 만들기 위해서는 빌드 작업을 해야만 한다.
이때 Visual Studio와 같은 IDE에서는 Debug 또는 Release 빌드 모드를 선택할 수 있다.
이는 사실 C/C++ 컴파일러의 최적화 옵션의 차이인데, Visual Studio에서는 편의를 위해 빌드 모드를 분리해놓은 것이다.
현업에 종사하거나 숙련된 개발자는 당연히 이 차이를 알고 있겠지만, 주로 C/C++ 개발을 처음 접하는 분들, 특히 학생들은 Visual Studio 기본값인 'Debug'로 빌드해서 배포를 하는 경우가 종종 있다.
잘못된 빌드 모드로 배포할 경우 성능 저하가 발생하거나 프로그램 실행 불가 등의 문제로 상당히 고생할 수 있으므로 각 빌드별 특징을 잘 알고있어야 한다.
차이점
Debug)
- 코드 최적화 하지 않음.
- 바이너리 (실행 파일) 크기가 크다.
- 코드 실행 속도가 느림
- 메모리 사용량이 많음.
- 바이너리에 디버깅에 필요한 정보가 포함됨.
- 컴파일 속도가 빠름
Release
- 코드 최적화 과정 수행.
- 바이너리 크기가 작다.
- 코드 실행 속도 빠름.
- 메모리 사용량이 적음.
- 디버깅에 필요한 정보가 거의 포함되지 않음.
- 최적화 과정이 포함되서 컴파일 속도 느림.
각각의 빌드 모드는 언제 사용해야할까?
Debug 빌드는 코드 실행 속도가 느리지만, 디버깅이 용이하고 컴파일 속도도 빠르므로 한창 개발이 진행중인 프로젝트에서 개발자가 디버깅을 할때 사용하는 것을 권장한다.
Release 빌드는 코드 실행 속도가 빠르고 배포하기도 용이하므로 (VC++ 재배포 패키지) 개발이 완료되고, 실제 사용자에게 전달할 때 사용하는 것을 권장한다.
그러나 Release 빌드에서도 Debug 빌드에서 확인되지 않은 문제점이 발견되므로 Release 빌드에서의 테스트도 필수라고 할 수 있다.
성능 비교
100,000개의 소수를 구하는 방식으로 Debug, Release 빌드 각각 테스트하여 비교해볼 것이다.
빌드 환경
- Windows 10 19H2 x64
- Visual Studio 2019
- Debug x86(/MDd /Od), Release x86(/MD /GL /O2 /Oi)
#include <iostream>
#include <vector>
#include <Windows.h>
bool isPrime(int x)
{
if (x <= 1)
{
return false;
}
for (int i = 2; i < sqrt(x); ++i)
{
if (x % i == 0)
{
return false;
}
}
return true;
}
int main()
{
int n;
std::vector<int> vec;
LARGE_INTEGER st, ed, freq;
std::cout << "소수 개수 : ";
std::cin >> n;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&st); // 측정 시작
for (int i = 0; vec.size() < n; ++i)
{
if (isPrime(i))
{
vec.push_back(i);
}
}
QueryPerformanceCounter(&ed); // 측정 완료
std::cout << "소요 시간 : " << (double)(ed.QuadPart - st.QuadPart) / ((double)freq.QuadPart);
system("pause");
return 0;
}
파일 크기
- Debug - 71.5KB (73,216 바이트)
- Release - 15.0KB (15,360 바이트)
=> 약 4.7배 차이
실행 속도 (100,000개, 3회 평균)
- Debug - 3.02014초
- Release - 0.24319초
=> 약 12.4배 차이
메모리 사용량
- Debug - 1,000KB
- Release - 844KB
=> 약 15%(156KB) 차이
(단, 실행 파일의 크기가 커질수록 오차가 발생할 것이다.)
Debug 모드에서는 컴파일 시 코드의 안정성을 위해 여러가지 장치를 추가
위는 Release 모드에서의 메모리 값이고 아래는 Debug 모드에서의 메모리값이다. 변수로 메모리 공간을 할당하고 그 값을 초기화시켜주기 전까지는 쓰레기 값이 들어가 있다. 이러한 쓰레기값이 문제를 일으킬만한 소지도 있어서 Debug에서는 이 문제를 방지하기 위해 cc로 메모리 값을 밀어버린다. Release모드의 경우 위 그림과 같이 쓰레기 값이 들어가 있는 반면 Debug에서는 아래 그림과 같이 쓰레기 값들을 전부 cc라는 값으로 초기화를 시켜준것을 볼 수 있다.
이것 이외에도 Stack의 위 아래 공간을 여유공간을 둬서 널널하게 크기를 잡는다던지 하는 여러가지 장치가 마련되어 있다. 사용하고 있는 메모리들의 간격을 바짝 붙여서 사용하지 않는다는 뜻이다. 또한 Debug모드는 컴파일 시 각종 Debug정보들을 넣어 디버깅이 편하게 만들어준다.
사용되는 런타임 라이브러리가 다르다
증분 링크 사용 여부가 다르다
디버그 모드의 경우 빌드를 생성할 때, 링커는 바이너리를 가능한 한 빠르게 링크하기 위해 패딩비트를 사용해서 어떤 함수가 변경되면 링커는 각 비트를 이동시켜서 변경된 부분만 삽입하는 형태가 아니라 그냥 전체를 오버라이드 해 버린다. 이때 변경된 함수는 기존에 남아있던 공간보다 커질 수 있기 때문에 이 경우에는 링커가 해당 바이너리를 다른 곳으로 옮겨야 하는데, 만약 새로운 곳으로 이동된 함수를 호출할때 실질적인 함수 주소를 호출한다면, 함수가 이동될 때 마다 새로운 링크 때문에 링커는 모든 CALL 명령을 조사해서 새로운 주소로 갱신해야 한다. 이때 사용되는 것이 증분 링크다. 함수들의 주소들을 테이블 형태로 구성하여 줌으로써 변경한 함수를 호출하는 부분의 코드를 재빌드할 필요 없이 함수 주소 테이블만 재 빌드 해주는 방식으로 빌드 타임을 줄일 수 있다. 또한 증분링크의 사용함으로써 디버그 모드에서 코드를 수정하면 디버그 창에서 계속해서 반영되게끔 만들 수 있다.
출처 : [C/C++] Debug 빌드 vs Release 빌드 차이점 정리 :: IT is Guide (tistory.com)
'CS > 공통' 카테고리의 다른 글
블록 암호 모드 (CFB, OFB, CTR) (0) | 2024.07.25 |
---|---|
사용자 인증(Authentication)과 권한 부여(Authorization)에 대한 이해 (0) | 2024.07.25 |
SDK, API의 개념과 차이점 (0) | 2023.10.09 |
모듈 Module (0) | 2023.08.24 |
컴포넌트 Component (0) | 2023.08.24 |