Precompiled header를 씁시다 (제발) by object

윈도우 프로그래머라면 precompiled header가 익숙할 것이다. stdafx.h 바로 그 녀석이다. C/C++ 컴파일러의 컴파일 단위는 무조건 .c/.cpp 하나씩이다 (여담으로 그래서 파일간, 즉 .obj 파일들 넘어서 최적화 하는 것이 쉽지 않다). 그래서 개개의 .c/.cpp 파일을 컴파일할 때 #include를 하는 모든 헤더를 당연히 매번 파싱 해야한다. 간단한 프로그램은 괜찮은데, windows.h 와 같이 덩치가 무지 큰 헤더 파일을 #include하는 모든 파일을 매번 컴파일하면 시간이 상당히 걸린다. Precompiled header는 이런 비용을 줄이기 위해 나온 것으로 특정 헤더 파일을 미리 파싱한 결과물을 .pch 파일로 덤프시킨다. 그리고 다른 파일들은 이 .pch를 단순히 사용함으로써 중복된 파싱 작업을 없앨 수 있다.

VC++는 디폴트로 이 precompiled header를 사용한다. Precompiled header가 작동하는 원리는 다음과 같다.

  1. 덩치가 크거나 거의 바뀌지 않는 정의 같은 것을 모두 stdafx.h에 넣는다.
  2. stdafx.cpp는 /Yc "stdafx.h" 옵션을 줘서 컴파일 한다. (Create)
  3. 다른 .cpp 파일들은 /Yu "stdafx.h" 옵션을 준다. (Use)

실제로 windows.h 같은 것을 사용하는 프로젝트에서 PCH를 쓰냐 안 쓰느냐는 컴파일 속도에 지대한 영향을 미친다. 하나 오해하지 말아야할 것은 precompiled header의 이름은 꼭 stdafx.h/cpp일 필요가 없다. 아무런 파일 이름도 된다.

그런데 리눅스 gcc도 precompiled header라는 것이 있다. 여기를 보면 분명히 지원을 한다. 직접 테스트는 해보지 않았지만 확장자를 .gch로 하면 gcc가 알아서 만들어 주는 것 같다. 그런데 지금까지 리눅스 프로젝트에서 PCH를 쓰는 경우를 거의 보지 못했다. 컴파일 시간을 매우 단축시킬 수 있는데 사람들이 몰라서 안 쓰는 것일까 아니면 컴파일 하는 동안 커피 한 잔의 여유라도 가지려고 안 쓰는 것일까? 당연히 인텔 C/C++ 컴파일러도 PCH를 지원한다.

 

실제로 PCH의 위력은 대단하다. 요즘 FTP 클라이언트인 FileZilla 3.0 소스를 받아서 좀 고칠 것이 있어서 삽질을 좀 했다. 100여개의 cpp 파일이 있는 작지 않은 규모이다. 컴파일 되는 모습을 보니 파일 하나당 1~2초씩 걸리는 것을 볼 수 있었다. 역시나 PCH를 사용하고 있지 않았다. 그래서 테스트를 좀 해봤다. 다행히 대부분의 모든 파일질라 소스들이 FileZilla.h라는 파일을 공통적으로 #include 하고 있었다. 그래서 이 파일을 PCH로 만들었다. 그랬더니 그 결과는?

파일질라 3.0 클라이언트에는 engine과 interface 두 프로젝트가 있는데 PCH가 없을 때는 50초와 55초의 컴파일 시간이 걸렸다. 그런데 PCH를 사용한 뒤에는 각각 16초와 27초로 거의 절반 이상의 시간이 절약됨을 볼 수 있었다. (사실 파일질라 클라이언트 소스는 윈도우용이라도 VC++에서 완벽히 링크까지 되지 않는다. 그래서 MinGW 깔고 캐삽질이 필요하다. 위의 시간은 대략 링킹을 제외한 대충 컴파일 되는데까지 측정한 시간이다.)

 

이런 비효율은 다른 곳에서도 쉽게 찾아볼 수 있다. 요즘 연구하는데 주로 들여다보는 벤치마크 프로그램 중 ITK 라는 것이 있다. 의료영상에 관련된 소프트웨어 툴킷이다. 이게 좀 무식하게 덩치가커서 툴킷과 Apps를 다 컴파일하면 하드 한 10기가를 차지 한다. 그 뿐만 아니라 컴파일을 다 하는데 40~50분은 족히 걸린다. 8코어 서버 머신에서 돌리면 그나마 빠르지만 듀얼 코어 정도에서 돌리면 세월아 네월아다.

그런데 역시 컴파일 되는 꼬라지를 보고 있으면 욕이 튀어나온다. 예를 들어, 의료영상 이미지 프로세싱 중에 Registration이라는 알고리즘이 있다. 이 알고리즘을 여러가지로 구현한 테스트 프로그램들이 ImageRegistration1, ImageRegistration2 이런식으로 15번까지 있다고 하자. 웃기게도 저 프로그램 자체는 모두 달랑 200줄도 안 되는 .cpp 파일 하나 씩으로 구성되어있다. 대부분은 공통적인 모듈을 그대로 사용한다. 그래서 같은 파일들이 죽으라고 계속 반복적으로 컴파일 된다. (더 최악인 건 최적화 옵션을 빡세게 주면 반복적인 동일한 최적화를 계속 한다는 것)

물론 이건 PCH를 쓴다고 다 해결되는 문제는 아니다. 분명 SharedLib을 씀에도 저런 불필요한 컴파일 작업이 반복되는 것은 다른 멍청함 때문일 것이다. 그러나 수 많은 헤더파일을 적어도 PCH로 만들면 분명히 컴파일 시간은 매우 단축될 것이다.

 

C/C++의 문제점 중 하나가 지저분한 헤더파일로 인한 얽히고 섥힌 의존관계로 길어지는 컴파일 시간일 것이다. 혹시 프로젝트 빌드 시간이 지나치게 길다면 한 번 중복되는 헤더파일들이 반복적으로 파싱이 되고 있지 않은지 살펴보고 이들을 다 PCH로 만들면 좋을 것이다. PCH를 쓰지 않는 덩치큰 프로젝트들을 규탄하는 바이다!! 오늘 하루 종일 컴파일 하다가 홀라당…

p.s. 참고로 VC++에서 특정 소스파일이 물고 있는 헤더파일 목록은 GUI 프로퍼티 페이지에서도 고칠 수 있고 (C/C++ – Advance – Show Includes) 혹은 /showIncludes 옵션으로도 볼 수 있다.

참고: GNU gcc에서 PCH 사용하기

사실 빌드 속도를 가장 높이기 쉬운 방법 중 하나는 IncrediBuild와 같은 툴을 이용하는 방법도 있다. 예전 회사에서 느린 빌드 속도에 답답할 때, 파일 하나씩을 회사의 모든 컴퓨터로 뿌린 다음에 컴파일해서 취합하면 정말 좋겠다라는 생각을 했었는데, 그 아이디어가 구현된 것이 IncrediBuild...


핑백

  • 코딩 공작소 : [펌] Precompiled header를 씁시다 (제발) 2008-07-07 17:51:40 #

    ... 씩을 회사의 모든 컴퓨터로 뿌린 다음에 컴파일해서 취합하면 정말 좋겠다라는 생각을 했었는데, 그 아이디어가 구현된 것이 IncrediBuild...스크랩: http://minjang.egloos.com/1956289 ... more

  • Mono log : gcc precompiled header 2008-08-18 17:17:32 #

    ... Precompiled header를 씁시다 (제발) gcc 3.4 부터 있는 precompiled header 사용방법 Using Precompiled Headers 좋은 동기 부여가 된 글입니다 ... more

덧글

  • xeraph 2008/06/30 14:34 # 답글

    아 gcc도 되는 것이었군요 (..);;
  • Gerald 2008/06/30 14:49 # 답글

    PCH 개념을 제대로 알고 가네요 ^^ 감사...
  • 우왕ㅋ굳ㅋ 2008/06/30 15:18 # 삭제 답글

    => 참고로 VC++에서 특정 소스파일이 물고 있는 헤더파일 목록은 GUI 프로퍼티 페이지에서도 고칠 수 있고

    요건 어디에 있나요? 못찾겠네요
  • object 2008/06/30 15:28 # 답글

    프로젝트 프로퍼티 (Alt+F7) 를 누르면 나옵니다. (C/C++ – Advance – Show Includes) 찾으실 수 있을 겁니다.
  • 최재훈 2008/06/30 17:59 # 삭제 답글

    PCH가 없으면 곤란하긴 하지만, 이게 또 설계상의 문제점을 감추는 데 주로 악용되기 때문에, 심경이 복잡하지요.
  • object 2008/07/01 01:27 #

    설계상의 문제점이라.. 확실하게 이해가는 대목은 아니지만 그런 면도 있나보네요. 의존성이 개판인 경우는 그것부터 제대로 정리를 해야겠죠. 그런데 뭐 forward declaration만 모아놓은 헤더파일만 있어도 클래스 수백개인 프로젝트도 별 문제는 없습니다만 ㅎㅎ
  • zelon 2008/06/30 23:35 # 삭제 답글

    제 홈페이지 중 위키에 있는 부분이라서 트랙백은 안되네요. 전에 적어둔 글 링크 하나 남깁니다.

    http://wimy.com/wiki/wiki.php/linuxProgramming#s-4

    gcc 로 precompiled header 쓰기입니다~
  • object 2008/07/01 01:26 #

    좋은 정보 감사합니다. 본문에 링크 추가하였습니다.
  • 몽몽이 2008/07/01 10:17 # 삭제 답글

    SSE, SIMD에 이어 깨달음을 주시는군요.
    VC++ 쓰던 시절에도 PCH 보믄 이거 뭥미? 수준이었는데...
    역시 다 이유가 있고 족보가 있는 물건이었군요.
    전 사실 일부러 없애고 쓴 적도 많았는데.
    아니나 다를까 공동으로 개발할땐 의존성이 개판인 코드를 만드는 사람들이 있어서...
    흠... 이제 알았으니 걍 버리지 말고 심각히 고려해야겠습니다~
  • object 2008/07/01 20:14 #

    그런 사람 있으면 데려다가 개인면담을 좀 나누시기를 바랍니다.

    예전 회사에 처음 들어갔을 때 소스코드를 보니 정말 개판이었는데요. 예를 들어, MFC가 만들어주는 CMainFrame mainfrm.h를 하나 고치면 거의 리빌드 올이 되었죠. 전혀 상관이 없어도 되는 파일도 그냥 인쿠르드 다 하고 정말 난리도 아니었습니다. 전방선언이 좀 문제를 일으키는 경우도 있으나 정말 이런 코딩 습관은 피해야죠.
  • 까막 2008/07/03 10:24 # 삭제 답글

    Apple의 XCode(툴체인이 gcc죠)를 써보면 기본으로 enable되어 있더군요. :)

    사실 PCH는 PCH를 끄면 컴파일이 잘되는 경우가 있어서 사람들이 '귀찮은' 기능정도로 인식 하더군요. 원리도 잘 모르기도 하고...

    gcc에서 PCH도입이 늦어진 이유는 아마 distcc(http://distcc.samba.org/)프로젝트때문이 아닌가 생각됩니다. IncrediBuild의 gcc버젼이랄까요. :) 왠만큼 큰 프로젝트들은 대부분 distcc를 활용하고 있더라구요.

    아 그리고, 제가 써본 버젼까지는.. IncrediBuild에서 PCH지원을 안했던걸로 기억합니다. :)


    ps. PCH도입이후 1시간안에 컴파일되는 솔루션을 보면서 행복했답니다. 잇힝
  • blueasa 2010/10/21 17:23 # 삭제 답글

    좋은 글 잘보고 갑니다. :)
  • shwlinux 2013/04/05 17:19 # 삭제 답글

    도움이 되었습니다!

    프로필을 보니 과 선배님이시네요~ 좋은 정보 감사드립니다.
  • 프로그래밍 입문자 2014/01/26 16:28 # 삭제 답글

    정말 감사합니다! 항상 비주얼스튜디오 6.0만 사용하다가 2010 버전으로 바꿨는데

    갑자기 PCH가 등장해서 당황했는데 덕분에 해결했네요.
  • changmin 2015/11/11 11:47 # 삭제 답글

    읽기 쉽게 정리해 주셔서 감사합니다. (_ _)
  • a 2017/10/17 20:00 # 삭제 답글

    웃긴건 ms 컨벤션인가 보면 PCH를 쓰지 말라고 되어있지요 ㅋㅋㅋ
댓글 입력 영역