Egloos | Log-in
F/OSS study
F/OSS study
[gcc] PCH(Pre-Compiled Header) 사용하기
Ubuntu: 9.04
gcc: 4.3.3-5ubuntu4


gcc-3.4 버전 이후부터 PCH 기능을 지원한다.
PCH의 기본 개념은 많은 소스 파일에서 공통적으로 참조하는 헤더 파일들을
(하나로 묶어) 미리 처리하여 컴파일 시간을 단축시키고자 하는 것이다.

PCH가 어떤 효과를 줄 수 있는지 알아보기 위해
가장 기본적인 hello 예제를 잠깐 살펴보자.

#include <stdio.h>

int main(void)
{
  printf("Hello, world\n");
  return 0;
}

위의 예제에서는 stdio.h 헤더 파일을 이용하고 있는데
stdio.h 파일 자체의 크기는 31KB에 달한다.
(위의 예제인 hello.c는 단지 80바이트 크기이다!!)

namhyung@NHK-XNOTE:~/temp/prog$ ls -lh /usr/include/stdio.h
-rw-r--r-- 1 root root 31K 2009-04-09 17:17 /usr/include/stdio.h

더욱 심각한 것은 stdio.h 파일 자체에서도
다른 여러 헤더 파일들을 #include 하고 있다는 것이다.
이를 확인해 보려면 다음과 같이 -H 옵션을 주어 gcc를 실행한다.

namhyung@NHK-XNOTE:~/temp/prog$ gcc -H hello.c
. /usr/include/stdio.h
.. /usr/include/features.h
... /usr/include/sys/cdefs.h
.... /usr/include/bits/wordsize.h
... /usr/include/gnu/stubs.h
.... /usr/include/bits/wordsize.h
.... /usr/include/gnu/stubs-32.h
.. /usr/lib/gcc/i486-linux-gnu/4.3.3/include/stddef.h
.. /usr/include/bits/types.h
... /usr/include/bits/wordsize.h
... /usr/include/bits/typesizes.h
.. /usr/include/libio.h
... /usr/include/_G_config.h
.... /usr/lib/gcc/i486-linux-gnu/4.3.3/include/stddef.h
.... /usr/include/wchar.h
... /usr/lib/gcc/i486-linux-gnu/4.3.3/include/stdarg.h
.. /usr/include/bits/stdio_lim.h
.. /usr/include/bits/sys_errlist.h
Multiple include guards may be useful for:
/usr/include/bits/stdio_lim.h
/usr/include/bits/sys_errlist.h
/usr/include/bits/typesizes.h
/usr/include/gnu/stubs-32.h
/usr/include/gnu/stubs.h

음.. 예상보다 많은 것 같다..;;
이제 여러분의 프로젝트에서 소스 파일이 수십~수백개 있으며
이들이 모두 (직/간접적으로) stdio.h 파일을 #include하고 있다고 생각해보자.

각 소스 파일을 컴파일할 때 마다
1. 매번 (최악의 경우 디스크에서) 위의 헤더 파일들을 모두 읽어온 후
2. 이를 파싱하여 전처리(preprocess)를 위한 준비를 마친 후에
3. 소스 파일을 전처리하고나서
실제 컴파일이 이루어 질 것이다.

PCH를 이용하면 1, 2번 과정을 미리 수행하여 파일로 저장해 두고
이 파일을 읽어들여 바로 3번 과정을 수행할 수 있기 때문에
컴파일 시간을 단축할 수 있다.

혹자는 전처리 과정을 조금 단축한다고 해서
컴파일 시간이 얼마나 차이가 나겠냐고 생각할지도 모르지만
실제로 PCH를 이용하여 컴파일 시간을 절반 이상 줄였다는 사례를 쉽게(?) 찾아볼 수 있으며
PCH를 사용하는 파일이 많아질수록 더욱 큰 성능 향상을 가져올 것이다.

하지만!!
전처리 과정 만 미리 수행했다고 하면
pre-compiled header라는 이름에게 조금 미안한 감이 있다.
실제로 PCH는 컴파일 과정도 수행한다.

이를 살펴보기 전에 먼저 PCH를 실제로 이용하는 법을 알아보자.
PCH 기능을 이용하려면 PCH로 이용할 헤더 파일을 하나 만들고
소스 파일에서는 이 헤더 파일을 #include 하도록 변경한다.
(보통 PCH로 이용할 파일에는 많이 이용되고 거의 변경되지 않으며
매번 #include해서 사용하기에는 부담스러운 헤더 파일들을 넣어두어야 한다.)

hello.h:
#include <stdio.h>

hello.c:
#include "hello.h"

int main(void)
{
  printf("Hello, world\n");
  return 0;
}

이제 PCH를 만들어야 하는데 이는 말 그대로 헤더 파일을 컴파일 하면 된다.

namhyung@NHK-XNOTE:~/temp/prog$ gcc hello.h
namhyung@NHK-XNOTE:~/temp/prog$ ls -lh hello.h*
-rw-r--r-- 1 namhyung namhyung   20 2009-06-21 13:06 hello.h
-rw-r--r-- 1 namhyung namhyung 801K 2009-06-21 13:06 hello.h.gch

헤더 파일을 컴파일하면 .gch 확장자가 붙은 파일이 생성되는데
이것이 바로 PCH 파일이다. (원본 파일과 크기를 비교해 보기 바란다.)
이제 소스 파일에서 hello.h 파일을 #include 할 때
hello.h.gch 파일이 있는지 검사하여 이를 우선적으로 사용한다.
(실제로 hello.c를 컴파일할 때 hello.h 파일이 없어도 된다.)

이제 hello.h 파일에 함수를 하나 추가해 보자.
#include <stdio.h>

static int add_pch_func(int a, int b)
{
  return a+b;
}

다시 hello.h 파일을 컴파일하는 데
이번에는 컴파일 과정을 자세히 보기 위해
다음과 같이 -v -save-temps 옵션을 이용한다.

namhyung@NHK-XNOTE:~/temp/prog$ gcc -v -save-temps hello.h
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.3.3-5ubuntu4' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-targets=all --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.3.3 (Ubuntu 4.3.3-5ubuntu4)
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-mtune=generic'
/usr/lib/gcc/i486-linux-gnu/4.3.3/cc1 -E -quiet -v hello.h -D_FORTIFY_SOURCE=2 -mtune=generic -fpch-preprocess -o hello.i  <== (1)
ignoring nonexistent directory "/usr/local/include/i486-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/i486-linux-gnu/4.3.3/../../../../i486-linux-gnu/include"
ignoring nonexistent directory "/usr/include/i486-linux-gnu"
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/usr/lib/gcc/i486-linux-gnu/4.3.3/include
/usr/lib/gcc/i486-linux-gnu/4.3.3/include-fixed
/usr/include
End of search list.
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-mtune=generic'
/usr/lib/gcc/i486-linux-gnu/4.3.3/cc1 -fpreprocessed hello.i -quiet -dumpbase hello.h -mtune=generic -auxbase hello -version -o hello.s --output-pch=hello.h.gch <== (2)
GNU C (Ubuntu 4.3.3-5ubuntu4) version 4.3.3 (i486-linux-gnu)
compiled by GNU C version 4.3.3, GMP version 4.2.4, MPFR version 2.4.0.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 0bf5703b57e064ca90b48f4e2c186f4a

여기서 눈여겨 볼 부분은 (1), (2)번 과정이다.
(1)번 과정은 cc1 -E 명령을 이용하여 전처리 과정을 수행하는 것인데
여기에 -fpch-preprocess 옵션이 추가되었으며 이 결과로 hello.i 파일이 생성된다.
(2)번 과정은 cc1 명령을 이용하여 컴파일 과정을 수행하는 것인데
-fpreprocessed 옵션을 추가한 후 hello.i 파일을 이용하여 hello.s (어셈블리) 파일을 생성한다.

hello.s에는 hello.h에 추가한 add_pch_func 함수의 내용이 들어있다.

namhyung@NHK-XNOTE:~/temp/prog$ cat hello.s
    .file "hello.h"
    .text
    .type add_pch_func, @function
add_pch_func:
    pushl %ebp
    movl %esp, %ebp
    movl 12(%ebp), %edx
    movl 8(%ebp), %eax
    addl %edx, %eax
    popl %ebp
    ret
    .size add_pch_func, .-add_pch_func
    .ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section .note.GNU-stack,"",@progbits

hello.h.gch 파일을 살펴보면 (이 경우에는 0x552f에서 찾을 수 있었다)
이 어셈블리 코드가 그대로 삽입되어 있는 것을 볼 수 있다.
(알다시피 ASCII 코드 0x09, 0x0a는 각각 '\t', '\n'에 해당한다.)

namhyung@NHK-XNOTE:~/temp/prog$ xxd -s0x552f -l192 hello.h.gch
000552f: 092e 7465 7874 0a09 2e74 7970 6509 6164 ..text...type.ad
000553f: 645f 7063 685f 6675 6e63 2c20 4066 756e d_pch_func, @fun
000554f: 6374 696f 6e0a 6164 645f 7063 685f 6675 ction.add_pch_fu
000555f: 6e63 3a0a 0970 7573 686c 0925 6562 700a nc:..pushl.%ebp.
000556f: 096d 6f76 6c09 2565 7370 2c20 2565 6270 .movl.%esp, %ebp
000557f: 0a09 6d6f 766c 0931 3228 2565 6270 292c ..movl.12(%ebp),
000558f: 2025 6564 780a 096d 6f76 6c09 3828 2565 %edx..movl.8(%e
000559f: 6270 292c 2025 6561 780a 0961 6464 6c09 bp), %eax..addl.
00055af: 2565 6478 2c20 2565 6178 0a09 706f 706c %edx, %eax..popl
00055bf: 0925 6562 700a 0972 6574 0a09 2e73 697a .%ebp..ret...siz
00055cf: 6509 6164 645f 7063 685f 6675 6e63 2c20 e.add_pch_func,
00055df: 2e2d 6164 645f 7063 685f 6675 6e63 0aff .-add_pch_func..


결론: PCH는 pre-compiled header가 맞구나.. -_-;;
by namhyung | 2009/06/21 13:43 | System | 트랙백(1) | 덧글(0)
트랙백 주소 : http://studyfoss.egloos.com/tb/5012866
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from tomato_house at 2010/02/09 17:14

제목 : [gcc] PCH(Pre-Compiled Heade..
[gcc] PCH(Pre-Compiled Header) 사용하기...more

:         :

:

비공개 덧글

◀ 이전 페이지 다음 페이지 ▶

카테고리
General
Application
System
Kernel
Book
Tips
태그
emacs binutils sed bash git SMP C script awk build block-layer algorithm x86 documentation kernel gcc synchronization scm CAaQA3 compiler CARM memory vcs glibc computer-architecture elf patch linux blktrace perf
전체보기
이글루 파인더

최근 등록된 덧글
1번은 call-stack 금방 확인이 ..
by 혁 at 11/13
해당 문법에 대한 자세한 가이드 같..
by ㅇㄷㅎ at 11/07
perf에 대한 설명 감사드립니다! ..
by flavono123 at 10/24
최근 등록된 트랙백
Tod's Ferrari Homme
by Tods Pas Cher,Kodak did ..
Mocassin Femme
by Mocassins Homme, I got so..
natural garcinia cambogia
by
rss

skin by jiinny


X