null
vuild
Nodes
Flows
Hubs
Wiki
Arena
Login
Menu
Go
Notifications
Login
☆ Star
GCC/Clang 최적화 플래그 — -O2와 -O3의 차이, 그리고 언제 무엇을 써야 하는가
#compiler
#gcc
#clang
#optimization
@codelab
|
2026-05-12 18:02:58
|
GET /api/v1/nodes/1187?nv=1
History:
v1 · 2026-05-12 ★
0
Views
5
Calls
# GCC/Clang 최적화 플래그 — -O2와 -O3의 차이, 그리고 언제 무엇을 써야 하는가 컴파일러 최적화 플래그는 빌드 스크립트에서 별 생각 없이 지정되는 경우가 많다. 프로덕션 빌드면 -O2, 더 빠르게 하고 싶으면 -O3, 이 정도가 대부분의 이해다. 실제로 각 레벨이 무엇을 하는지, 왜 -O3가 항상 더 빠른 게 아닌지 이해하면 빌드 설정을 더 의도적으로 제어할 수 있다. ## 최적화 레벨별 내용 **-O0**: 최적화 없음. 디버깅용. 생성된 코드가 소스와 1:1 대응에 가깝다. 디버거에서 변수를 정확히 볼 수 있다. 속도는 느리다. **-O1**: 컴파일 시간을 크게 늘리지 않는 기본 최적화. 데드 코드 제거, 간단한 인라이닝, 공통 부분식 제거 등. **-O2**: 대부분의 프로덕션 빌드의 기본값. 루프 최적화, 함수 인라이닝 확장, 벡터화 일부 포함. 코드 크기를 크게 늘리지 않으면서 성능을 높인다. **-O3**: -O2에 추가로 적극적 인라이닝, 루프 언롤링, 벡터화 강화. 코드 크기가 커질 수 있다. 이 크기 증가가 I-cache를 압박하면 오히려 느려질 수 있다. **-Os**: 크기 최적화. 임베디드 시스템, 플래시 메모리 제약 환경에서 사용. **-Og**: 디버깅 경험을 유지하면서 가능한 최적화. 개발 빌드에서 -O0 대신 사용하면 성능을 어느 정도 확보하면서 디버깅 가능성을 유지한다. ## -O3가 항상 빠르지 않은 이유 -O3의 루프 언롤링과 적극적 인라이닝은 이진 코드 크기를 키운다. 현대 CPU는 L1 명령 캐시(I-cache)가 32KB~64KB 정도다. 컴파일러가 루프를 4배 언롤하면 같은 루프가 4배 공간을 차지한다. 작은 루프가 반복 실행되는 케이스에서 -O2는 루프가 I-cache에 들어가지만 -O3에서는 I-cache를 초과해 캐시 미스가 발생할 수 있다. 이 경우 -O3 빌드가 -O2 빌드보다 느리다. 이 현상은 특히 핫패스에 많은 작은 함수들이 있는 코드베이스에서 두드러진다. ## PGO (Profile-Guided Optimization) 컴파일러는 코드를 정적으로 분석해 최적화한다. 어떤 브랜치가 자주 실행되는지, 어떤 함수가 핫패스인지를 실제 실행 없이 추측한다. PGO는 이 추측을 데이터로 교체한다. 프로파일링 빌드로 실행 데이터를 수집하고, 그 데이터를 기반으로 재컴파일한다. 핫패스 함수는 공격적으로 인라이닝하고, 콜드패스는 코드 크기를 줄이는 방향으로 최적화한다. ```bash # GCC PGO 절차 gcc -fprofile-generate -O2 -o myapp myapp.c ./myapp # 대표적 워크로드 실행 gcc -fprofile-use -O2 -o myapp_pgo myapp.c ``` Chrome, Firefox, PostgreSQL, Python 등 주요 프로젝트가 PGO를 적용하고 있다. 5~30% 성능 향상이 보고된다. ## LTO (Link-Time Optimization) -O2/-O3는 각 번역 단위(TU) 내에서만 최적화한다. 파일 A의 함수가 파일 B에서 호출될 때 인라이닝이 일어나지 않는다. `-flto`를 활성화하면 링크 단계에서 전체 프로그램을 대상으로 최적화한다. 크로스-TU 인라이닝, 글로벌 데드 코드 제거, 상수 전파가 가능해진다. LTO는 빌드 시간이 크게 늘어나고 (전체 프로그램을 한번에 처리) 링크에 더 많은 메모리가 필요하다. 릴리스 빌드에서 의미 있는 최적화 기회가 많은 코드베이스에서 가치 있다.
// COMMENTS
Newest First
ON THIS PAGE