2009.07.03 00:03 책 정리/C++ Template

아니 왜 템플릿에서 미리 컴파일된 헤더 기법을 다루지? 라고 스스로 자문을 구해 보았었다. 처음에는 별 생각이 없었지만, 지속된 질문을 통해 한가지 사실을 알게 되었다. 템플릿 코드들은 대부분 "포함모델"로 코드를 관리 된다.

그러므로 모두다 헤더 파일 이란 뜻!!!!! 그렇다! 미리 헤더 파일을 컴파일 함으로써, 가뜩이나 템플릿 때문에 컴파일 타임 잡아 먹는다고 팀원들에게 눈치 보는데, 이참에 얍실스럽게 , "나 때문에 컴파일 타임 줄어들었잖아~" 라고 웃어 줄 수 있다는 것이다!!!

미리 컴파일된 헤더 기법이란 무엇인가?
헤더 파일을 미리 컴파일 해 두어, 컴파일 시간을 줄이는 기법이다. 이 기법은 표준이 아니므로, 컴파일러 제작자들마다 구현 방법 및 적용 방법이 다르다. 자세한 내용은 자신이 이용하는 컴파일러의 "미리 컴파일된 헤더" 조항을 찾아 보는게 좋겠다. 참고로 msvc, g++ 는 미리 컴파일된 헤더 기법을 지원한다. 물론 사용 방법은 다르다.

일반적으로 "미리 컴파일된 헤더 : PreCompiled Header"는 다음과 같은 원리로 동작한다.

1. 미리 컴파일 될 헤더 파일의 모든 코드들을 읽어 들인다.
2. 각 코드들의 토큰을 이해하여, 기호 테이블에 올려 둔다.
3. 2번을 컴파일하여 기계어파일을 저장한다.
4. 다른 파일의 모든 코드들을 읽어 들인다.
5. 이때 파일의 시작부분이 3번의 기계어 파일과 같은지 비교 한다.
6. 같다면 3번을 갖다 쓰고, 다른 부분을 컴파일 한다.
7. 4번부터 다시 돌며, 모든 파일을 컴파일 한다.

여기서 잠깐! 컴파일러 제작사마다 구현 및 적용이 다르다고 분명히 지적했다. 내가 알고 있는 것은 g++, msvc 뿐이니 대강 설명하고 나머지는 링크로 대체 한다.

msvc의 경우
1. 단 1개의 미리 컴파일된 헤더를 지원한다. 즉, 미리 컴파일 할 수 있는 헤더는 1개 뿐이라는 것. 그러므로 이 헤더에 모든 미리 컴파일 할 헤더 파일을 다 때려 박아 두어야 한다.
2. 헤더명은 자동으로 StdAfx.h 로 정해 진다. 물론 유저가 바꿀 수 있다.
3. 미리 컴파일된 헤더 파일은 확장자가 pch 이다.
4. 이 기법을 사용 할 땐, 컴파일할 c/cpp 파일에 모두 #include "StdAfx.h" 를 해 주어야 한다.
5. /MP 옵션과 중복되지 못한다. 즉, 멀티 프로세싱 컴파일을 하지 못한다. 즉, 1개의 코어만으로 컴파일 된다.

g++의 경우
1. 역시 단 1개의 미리 컴파일된 헤더를 지원 한다. 테스트로 여러개의 미리 컴파일된 헤더를 만들어 테스트 해보았지만, 컴파일 시간이 늘었으면 늘었지 줄지는 않았다.
2. 헤더파일을 단순히 컴파일 하기만 하면 된다. 예) g++ -o abcdefg.h, g++ 4.1.2 버전 기준
3. 그러면 pch 확장자를 가진 미리 컴파일된 헤더 파일이 컴파일 된다, 예) abcdefg.pch
4. 미리 컴파일된 헤더를 사용 할 파일의 최상단에 미리 컴파일 했던 헤더 파일명을 포함 시키면 된다. 예) #include "abcdefg.h"
5. 병렬 컴파일은 아마도 될 것이다. 왜냐하면 병렬 컴파일은 make가 담당해 주기 때문이다 : ) ... 실제로 테스트 했을 때 아무 에러 메세지도 없었으니 뭐 되는 거겠지. ㅋㅋ

미리 컴파일된 헤더라고 해서 장점만 있는 것이 아니다. 다음의 단점들이 있다.
1. 프로젝트 초기, 미리 컴파일 할 헤더 파일이 자주 변경 되면, 오히려 컴파일 시간이 더 오래 걸린다. 왜냐하면 최초 1회 미리 컴파일된 헤더 파일을 만들 때, 20% ~ 200% 느리기 때문이라고 책에 적혀있다. (체감 할 정도는 아니였으므로, 테스트 해보지도 않았다.)
2. 헤더 파일간의 의존관계를 거의 없애면서 프로젝트를 구성 시켜 놓으면 오히려 병열 컴파일이 더 빠르다. 예를 들어 서버 컴퓨터의 CPU 가 8개라면, 일반 컴퓨터에선 24분 걸리는게 1분 30초로 줄어 들기 때문이다. ..... (그래서 리눅스에서 테스트 할 필요가 없었다.. 구차한 변명)


여담으로,
분산 컴파일 쪽으로 컴파일 타임을 기하급수(..는 오버다..)적으로 줄이고 싶다면, 윈도우 환경에선 인크리드 빌드(유료 300달라던가.), 리눅스에선 distcc(무료!!!!!)를 알아 보는게 더 도움이 될 것이다. 참고로 미리 컴파일 된 헤더를 분산 컴파일 쪽에서 지원을 못해 주었던 것으로 기억한다. 앞으로 지원해 준다는 이야기는 들었는데 지금은 모르겠다. : )

부수적 이야기
컴파일러가 미리 컴파일된 헤더 지원 갯수가 1개 밖에 되지 않는 이유에 대해서 무척 궁금했었다. 오늘 샤워를 하다가 그 이유를 알 것 같다. 바로 여러개의 미리 컴파일된 헤더는 서로에게 영향을 줄 수 있는 경우에 대처 할 수 없기 때문이다. 왜 대처 할 수 없냐구?  이미 컴파일이 되었잖아!! 예를 들어  #define 이라든지, 상호 참조라든지 중복재정의가 일어 날 수도 있는 상황이기에 어쩔수 없이  1개의 미리 컴파일된 헤더를 지원했던것 같다. 컴파일러 개발자가 개발 말고 부업을 하고 있으니 어쩔수 없지 아니한가. 훗날을 기약해야지.

이 생각은 왜 미리 커파일된 헤더를 컴파일러는 기본적으로 지원하지 않는가? 와 같다. 그러고 보니 msvc에서 mfc의 경우 이미 어떻게 짜여질지 결정된 상태이기 때문에 가능할 수 있었다고 생각 한다


이것으로 템플릿과 궁합이 좋을만한 "미리 컴파일된 헤더"기법에 대해서 정리 했다.


저작자 표시
신고
posted by 농사를 짓는 게임 프로그래머 최익필

댓글을 달아 주세요