최적화 플래그는 코드를 바꾸지 않고도 생성되는 기계어와 성능을 바꾸는 첫 단계입니다. 같은 소스에 -O2와 -O3만 바꿔도 벤치마크 결과가 수 퍼센트에서 수십 퍼센트까지 달라질 수 있으며, 잘못 선택하면 코드 크기 증가로 오히려 느려지는 회귀도 발생합니다. 이 챕터에서는 GCC/Clang의 -O0~-Ofast와 MSVC의 /O1·/O2·/Ox 의미, trade-off, 언제 어떤 레벨을 쓸지/피할지 판단 기준, 그리고 릴리즈/디버그/프로파일 빌드 전략을 정리합니다.
왜 최적화 플래그인가 (동기)
컴파일러는 소스를 **중간 표현(IR)**으로 바꾼 뒤, 수십 가지 최적화 패스(인라이닝, 루프 최적화, 레지스터 할당, 벡터화 등)를 적용해 기계어를 만듭니다. 어떤 패스를 켤지는 최적화 레벨 한 번으로 제어할 수 있어, 코드 수정 없이 빌드 옵션만으로 성능을 탐색할 수 있습니다. 레벨을 올리면 더 많은 최적화가 켜지지만 컴파일 시간·메모리·코드 크기가 늘고, 드물게 특정 코드에서 성능이 나빠지기도 합니다. 그래서 이 챕터의 핵심은 “무조건 높은 레벨"이 아니라 측정 후 선택입니다.
역사·배경 (최적화 레벨의 등장)
GCC에서는 1990년대부터 -O, -O2 등 최적화 레벨이 도입되었고, -O3는 더 공격적인 최적화를 묶은 것으로 확장되었습니다. GCC 공식 문서는 -O3가 -O2의 모든 최적화에 더해 추가 패스를 켠다고 설명합니다.
“Optimize even more. -O3 turns on all optimizations specified by -O2 and also turns on the -finline-functions, -funswitch-loops, -fpredictive-commoning, -fgcse-after-reload, -ftree-vectorize and other options.” — GCC Manual, Optimize Options
레벨이 올라갈수록 추가로 켜지는 옵션이 문서에 나열되므로, 정확한 목록은 사용 중인 GCC 버전의 문서를 참고하는 것이 좋습니다. -Os(크기 최소화)는 임베디드·제한된 환경을 위해 추가되었고, -Ofast는 GCC 4.x 대에서 부동소수점 완화 옵션을 묶은 레벨로 등장했습니다. Clang은 GCC와 호환되는 레벨 이름을 유지하면서 내부적으로 다른 최적화 순서·휴리스틱을 쓰며, MSVC는 /O1, /O2, /Ox 등 자체 명칭을 사용합니다. 오늘날 대부분의 C/C++ 프로젝트는 -O2 또는 /O2를 릴리즈 기본값으로 두고, 필요 시 -O3를 벤치마크와 함께 검증하는 패턴이 널리 쓰입니다.
최적화 레벨 정의 (GCC / Clang)
최적화 레벨은 “어떤 패스를 얼마나 공격적으로 켤지"를 한 번에 지정하는 옵션입니다. 큰 흐름은 -O0(없음) → -O1(기본) → -O2(균형, 릴리즈 권장) → -O3(공격적) 으로 점점 더 많은 최적화가 켜지고, 여기서 갈라져 나온 -Os는 크기를, -Ofast는 부동소수점 완화를 통한 속도를 우선합니다. 각 레벨의 목적과 켜지는 최적화 방향은 다음과 같습니다.
- -O0: 최적화 없음. 디버깅 시 변수·소스 라인 대응이 정확하고 컴파일이 빠르지만, 생성 코드는 느립니다. 디버그 빌드 기본값으로 적합합니다.
- -O1: 기본적인 최적화만 적용. 컴파일 시간을 크게 늘리지 않으면서 일부 성능 이득을 얻을 때 사용합니다. 인라인은 제한적, 루프·레지스터 최적화가 일부 켜집니다.
- -O2: 대부분의 릴리즈 빌드 권장 레벨입니다. 인라이닝, 루프 최적화, 레지스터 할당 등 대다수 최적화가 켜지고, -O3에 비해 공격적인 변환은 적어 코드 크기·컴파일 시간·회귀 위험이 상대적으로 낮습니다.
- -O3: -O2에 더해 더 공격적인 인라이닝, 루프 언롤링, 예측 실행 관련 최적화가 추가됩니다. 수치 연산·벡터화에 유리한 코드에서는 -O2 대비 눈에 띄게 빨라질 수 있지만, 코드 크기가 커지고 컴파일 시간·메모리 사용량이 늘며, 드물게 성능 회귀가 나옵니다.
- -Os: 코드 크기를 줄이는 방향으로 최적화합니다. 인라이닝·언롤링을 억제해 바이너리 크기를 줄이므로, 임베디드·캐시가 극히 제한된 환경에서 고려합니다.
- -Ofast: -O3에 더해 부동소수점 연산의 표준 준수를 완화합니다(-ffast-math 계열). 수치적으로 더 빠른 코드가 나올 수 있지만, 반올림·연산 순서에 따라 결과가 달라질 수 있어 일반 코드에서는 사용을 피하는 것이 안전합니다.
MSVC 대응
MSVC는 /O1(크기 최소화), /O2(속도 최대화, 기본 릴리즈), /Ox(최대 최적화) 등으로 비슷한 역할을 합니다. /O2가 GCC/Clang의 -O2에 대응하는 기본 릴리즈 설정으로 널리 쓰이며, Visual Studio 프로젝트 속성에서 “Optimization"을 “Maximize Speed (/O2)“로 두면 됩니다.
최적화 레벨별로 켜지는 최적화 (개념)
레벨이 올라갈수록 인라이닝·루프 최적화·벡터화 등이 더 공격적으로 적용됩니다. 개념적으로는 아래와 같습니다.
flowchart LR
subgraph low ["-O0 / -O1"]
A["최소 최적화"]
B["디버깅 용이"]
end
subgraph mid ["-O2"]
C["인라이닝·루프·레지스터"]
D["일반 릴리즈 권장"]
end
subgraph high ["-O3 / -Ofast"]
E["공격적 인라이닝·언롤링"]
F["코드 크기·회귀 주의"]
end
low --> mid --> high
실제로 켜지는 플래그 목록은 컴파일러·버전마다 다르므로, gcc -O2 -Q --help=optimizers나 문서로 확인합니다(개별 플래그 확인 방법은 아래 「개별 최적화 플래그와 확인 방법」 절 참고).
-O2 vs -O3 trade-off
대부분의 선택은 -O2와 -O3 사이에서 일어나며, 둘의 차이는 단순한 “속도 차이"가 아니라 속도·코드 크기·컴파일 시간·회귀 위험의 묶음입니다. 핵심은 -O3가 항상 빠른 것은 아니라는 점으로, 코드 크기가 커지면 명령어 캐시(I-cache) 압박으로 오히려 느려질 수 있습니다.
- 속도: 벡터화·인라이닝에 민감한 핫 루프가 있으면 -O3가 유리한 경우가 많습니다. 차이는 워크로드에 따라 0%에 가깝게 나올 수도, 두 자릿수 %까지 나올 수도 있습니다.
- 코드 크기: -O3는 인라이닝·언롤링으로 텍스트 세그먼트가 커져 I-cache 압박이 생길 수 있어, 작은 핫 루프가 많은 경우 오히려 느려질 수 있습니다.
- 컴파일 시간: -O3가 더 오래 걸리고, 대형 프로젝트에서는 빌드 시간이 눈에 띄게 늘어납니다.
- 회귀: -O3에서만 발생하는 회귀는 드물지만 보고되는 편이므로, 릴리즈 전에 동일 벤치마크로 -O2 vs -O3를 비교해 두는 것이 좋습니다.
-Ofast와 부동소수점
-Ofast는 -O3에 -ffast-math 계열 옵션을 더한 레벨입니다. -ffast-math는 -fno-math-errno, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans, -fassociative-math 등 여러 하위 옵션의 묶음으로, 부동소수점 연산의 표준 준수를 완화합니다. 예를 들어 연관 법칙을 가정하면 (a + b) + c와 a + (b + c)를 같은 것으로 간주해 순서를 바꿀 수 있어 벡터화가 더 잘 들어가지만, 부동소수점에서는 순서에 따라 결과가 달라질 수 있습니다.
따라서 재현성이 중요한 시뮬레이션·금융·규제 제출 코드에서는 -Ofast를 쓰지 않고 -O3까지로 제한합니다. 신호 처리·이미지 처리처럼 오차 허용 범위가 넓은 도메인에서는 -Ofast로 속도 이득을 취하는 경우가 있습니다. 위험을 줄이려면 -O3에 -ffast-math 전체 대신 -fno-math-errno 같은 개별 옵션만 선택적으로 켜는 방법도 있습니다.
빌드 명령과 크기·시간 비교 (예시)
동일한 소스에 다른 최적화 레벨을 적용해 컴파일 시간과 바이너리 크기를 비교하는 예시입니다.
| |
빌드 시스템에서는 빌드 타입으로 레벨을 묶어 관리합니다. CMake의 표준 빌드 타입은 다음과 같습니다.
| |
측정과 비교
레벨 선택은 추측이 아니라 측정으로 합니다. 동일한 벤치마크 스위트를 각 플래그로 빌드해 반복 실행하고, 한두 번의 실행이 아니라 평균·표준편차(또는 백분위)를 비교해야 의미 있는 판단이 됩니다. 벤치마크가 핫패스를 잘 커버하지 못하면 레벨 차이가 드러나지 않습니다.
- 컴파일 시간: 동일 소스에 -O0 / -O2 / -O3를 적용해 측정합니다. 대형 TU에서 차이가 큽니다.
- 실행 파일 크기:
size로 텍스트·데이터·bss 크기를 비교합니다. -Os < -O2 < -O3 순으로 커지는 경향이 있습니다. - 벤치마크: 각 플래그 빌드를 반복 실행해 평균·분포를 비교합니다.
- 회귀 테스트: 릴리즈 브랜치에서는 레벨을 고정하고, 코드 변경 시 동일 벤치마크로 성능 하락이 없는지 CI에서 주기적으로 확인합니다.
수치를 해석하는 예를 들면, 동일 벤치마크를 -O2와 -O3로 각각 10회 실행해 -O2: 100µs, -O3: 85µs를 얻었다면 약 15% 개선입니다. 이때 (1) 표준편차·백분위로 차이가 통계적으로 의미 있는지 확인하고, (2) 같은 스위트의 다른 케이스에서 -O3가 느려진 구간이 없는지 보고, (3) -O3 바이너리의 텍스트 세그먼트가 크게 늘었는지 확인합니다. 모든 구간에서 이득이 있거나 동등하고 크기 증가가 수용 가능하면 -O3를 채택하고, 한 구간이라도 나빠지면 원인(예: I-cache 압박)을 분석하거나 해당 모듈만 -O2로 둡니다. (측정값은 CPU·컴파일러·플래그에 따라 다릅니다.)
간단한 예시로 레벨별 차이 확인
작은 합산 루프 하나로도 레벨 차이를 체감할 수 있습니다. 아래 코드를 -O0와 -O2로 빌드해 실행 시간을 비교해 봅니다.
| |
-O0에서는 루프가 최소 최적화만 받아 상대적으로 느리고, -O2에서는 루프 최적화·인라이닝으로 훨씬 빠른 기계어가 생성됩니다. 실제 측정 시에는 Google Benchmark, nanobench 등으로 반복 실행 후 평균·분포를 보고, 대형 TU에서는 time g++ -O0 -c big.cc vs -O2로 컴파일 시간 차이도 확인할 수 있습니다.
릴리즈/디버그/프로파일 빌드 전략
빌드 목적에 따라 레벨을 나누는 것이 기본 원칙입니다. 디버그는 정확한 단계 실행을, 릴리즈는 성능을, 프로파일 수집은 계측 비용과 대표성의 균형을 우선합니다.
- 디버그: -O0(또는 -O1)에
-g를 함께 써서 단계 실행·변수 조사에 적합하게 유지합니다. - 릴리즈: -O2를 기본으로 하고, 핫 경로가 수치·벡터화에 민감하면 -O3를 시도한 뒤 벤치마크로 검증합니다. 회귀가 없을 때만 -O3를 채택합니다.
- 프로파일 수집: PGO 1단계처럼 instrumented 빌드에서는 -O2 수준으로 두고, 프로파일 기반 최적화 단계에서 -O3를 쓰는 조합이 흔합니다.
LTO·PGO와의 조합도 같은 원리입니다. LTO(챕터 02)를 쓸 때도 각 TU는 먼저 -O2/-O3로 IR을 생성하고 링크 시점에 합쳐 추가 최적화하므로 -O2 -flto 또는 -O3 -flto 조합이 일반적이며, PGO(챕터 03)는 1단계를 -O2로, 2단계를 -O3로 두는 패턴이 흔합니다. 어느 경우든 동일 벤치마크로 측정해 회귀가 없는지 확인합니다.
빌드 시스템별로는 빌드 타입을 다음과 같이 매핑하고, CI에서는 릴리즈 빌드로 회귀를 측정합니다.
| 빌드 시스템 | 릴리즈(-O2) | 디버그(-O0) | RelWithDebInfo |
|---|---|---|---|
| CMake | CMAKE_BUILD_TYPE=Release | CMAKE_BUILD_TYPE=Debug | CMAKE_BUILD_TYPE=RelWithDebInfo |
| Meson | buildtype: ‘release’ | buildtype: ‘debug’ | buildtype: ‘debugoptimized’ |
| Make | CXXFLAGS=-O2 | CXXFLAGS=-O0 -g | CXXFLAGS=-O2 -g |
CMake의 Release 기본이 -O3인 경우도 있으므로, 프로젝트에서 add_compile_options(-O2) 등으로 명시적으로 고정해 두면 환경에 덜 흔들립니다.
개별 최적화 플래그와 확인 방법
최적화 레벨은 수십 개의 개별 -f* 플래그를 묶은 것입니다. -O2는 대략 -finline·-foptimize-sibling-calls 등을 켜고, -O3는 여기에 -finline-functions(더 공격적 인라이닝)·-funswitch-loops·-ftree-vectorize 등이 더해진 묶음으로 이해할 수 있습니다. 실무에서는 보통 -O2 또는 -O3만 지정하고 개별 플래그를 건드리지 않는 편이 유지보수에 유리하지만, 특정 최적화에서만 버그가 재현된다면 해당 최적화만 끄는 -fno-* 플래그로 회귀를 피할 수 있습니다.
어떤 최적화가 실제로 켜지는지 확인하려면 컴파일러 도움말을 사용합니다.
- GCC:
gcc -O2 -Q --help=optimizers로 -O2에서 켜지는/꺼지는 최적화 목록을 보고,-O3와 diff해 차이를 비교합니다. - Clang:
clang -O2 -mllvm -debug-pass=Structure(버전에 따라 다름)로 패스 구성을 보거나 공식 문서로 -O2/-O3 차이를 확인합니다. - MSVC: 프로젝트 속성 “Optimization” 하위에서 “Inline Function Expansion”, “Enable Intrinsic Functions” 등 개별 옵션을 확인합니다.
한눈에 보기: 최적화 레벨 비교
지금까지의 정의·트레이드오프를 한 표로 정리하면 다음과 같습니다. 인라이닝·벡터화 열은 “얼마나 공격적인지"의 개념적 방향이며, 실제 켜지는 플래그는 컴파일러·버전에 따라 다릅니다.
| 레벨 | 목적 | 인라이닝/벡터화 | 코드 크기 | 컴파일 시간 | 권장 용도 |
|---|---|---|---|---|---|
| -O0 | 없음 | 거의 없음 | 작음 | 빠름 | 디버그 빌드 |
| -O1 | 기본 | 제한적 | 보통 | 보통 | 빠른 빌드 + 일부 이득 |
| -O2 | 균형 | 일반적 | 보통 | 보통 | 일반 릴리즈 기본 |
| -O3 | 최대 속도 | 적극적·언롤링 | 큼 | 느림 | 수치/벡터화 핫패스, 벤치 검증 후 |
| -Os | 크기 최소 | 억제 | 작음 | 보통 | 임베디드·제한된 캐시 |
| -Ofast | 수치 최대 | -O3+추가 | 큼 | 느림 | 신호/과학 계산만, 재현성 불필요 시 |
판단 기준: 언제 쓸지 / 언제 피할지
| 상황 | 권장 | 비권장 |
|---|---|---|
| 일반 릴리즈 빌드 | -O2 | -O0, -Ofast(일반 코드) |
| 수치·벡터화가 중요한 핫 루프 | -O3 (벤치로 검증 후) | -O3 무검증 채택 |
| 디버깅·단계 실행 | -O0 또는 -O1 | -O2 이상 |
| 임베디드·바이너리 크기 제한 | -Os 또는 -O2 | -O3 |
| 금융·규제·재현성 중요 | -O2 또는 -O3 (fast-math 없음) | -Ofast |
| PGO 1단계(instrumented) | -O2 수준 | -O0(너무 느림), -O3(과도) |
적용 체크리스트: (1) 릴리즈 기본은 -O2로 두고, -O3는 벤치마크로 이득과 회귀를 확인한 뒤 도입한다. (2) -Ofast는 부동소수점 재현성이 필요 없는 도메인에서만 사용한다. (3) CI에서 릴리즈 빌드로 성능 회귀를 주기적으로 측정한다.
실전 시나리오: -O2에서 -O3 전환 검토
릴리즈가 -O2로 설정되어 있고 핫패스에 수치 연산·루프가 많은 프로젝트에서 -O3 전환을 검토하는 흐름은 다음과 같습니다.
- 벤치마크 준비: 핫패스를 잘 커버하는 벤치마크(또는 통합 테스트의 성능 구간)를 확보합니다.
- 빌드: 동일 소스로 -O2와 -O3 각각 빌드해 두 바이너리를 만듭니다.
- 실행·측정: 같은 벤치마크를 여러 번 실행해 평균·표준편차(또는 백분위)를 기록합니다.
- 크기 확인:
size로 텍스트 세그먼트 증가를 봅니다. 크게 늘고 지연이 중요한 경로가 여러 개면 I-cache 압박 가능성을 염두에 둡니다. - 판단: 속도 이득이 명확하고 다른 구간의 회귀가 없으면 -O3로 전환하고, 이득이 미미하거나 회귀가 있으면 -O2를 유지합니다.
- CI 반영: 선택한 레벨로 릴리즈 빌드 후, CI에서 -O2와 -O3 결과를 함께 수집해 -O3에서만 느려지는 회귀를 자동으로 감지합니다. 일정 비율 이상 나빠지면 실패로 처리하고 빌드 로그·벤치 결과를 아티팩트로 보관합니다.
자주 하는 실수
- -O3를 검증 없이 릴리즈에 넣는 경우: 일부 코드에서 코드 크기 증가로 느려질 수 있으므로, 동일 벤치마크로 -O2 vs -O3를 비교한 뒤 채택한다.
- -Ofast를 일반 비즈니스 로직에 쓰는 경우: 부동소수점 결과가 바뀔 수 있어, 재현성·규제가 중요한 코드에서는 사용하지 않는다. 테스트가 비트 단위 기대값을 비교한다면 허용 오차(epsilon)를 두거나 해당 모듈만 -O3로 둔다.
- 디버그 빌드에 -O2 이상을 쓰는 경우: 변수가 “optimized out"으로 사라지거나 라인 대응이 어긋나 디버깅이 어려워진다. 디버깅 시에는 -O0 또는 -O1 + -g를 유지하고, 릴리즈 동작 재현이 필요하면 RelWithDebInfo(-O2 -g)를 쓰되 일부 변수는 여전히 보이지 않을 수 있음을 받아들인다.
비판적 시각: 한계와 트레이드오프
최적화 레벨은 높을수록 좋다가 아닙니다. -O3는 코드 크기를 키워 I-cache 미스를 늘릴 수 있고, -Ofast는 수치 결과를 바꿉니다. “릴리즈 = -O3"로 고정하기보다, 프로젝트의 핫패스와 벤치마크로 한 번씩 측정한 뒤 선택하는 것이 안전합니다. 레거시 프로젝트라면 이미 -O2로 안정적으로 돌고 있을 때 무리하게 -O3로 올리지 말고, 성능 이득이 명확할 때만 단계적으로 전환합니다. 이미 -O3인데 특정 TU만 문제를 일으키면 그 TU만 -O2로 컴파일하도록 예외를 둘 수 있습니다(CMake: set_source_files_properties(... COMPILE_FLAGS -O2)).
자주 묻는 질문
- Q: 릴리즈는 무조건 -O3가 맞나요? A: 아닙니다. -O2를 기본으로 하고, 벤치마크로 -O3 이득이 있고 회귀가 없을 때만 -O3를 씁니다.
- Q: -Os와 -O2 중 무엇을 써야 하나요? A: 바이너리 크기나 I-cache가 극히 제한된 환경(일부 임베디드)이면 -Os를, 그렇지 않으면 보통 -O2가 유리합니다.
- Q: -Ofast를 썼을 때 테스트가 실패합니다. A: 부동소수점 비교가 비트 단위로 맞지 않을 수 있습니다. 허용 오차를 두거나, 해당 모듈만 -O3로 두고 -Ofast를 제거합니다.
- Q: CMake Release가 -O3인데 -O2로 고정하려면? A:
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")처럼 Release 플래그를 덮어씁니다. - Q: 컴파일 시간을 줄이려면? A: -O3 대신 -O2를 쓰면 줄어듭니다. 캐시·증분 빌드는 챕터 12에서 다룹니다.
Tr.02(인라이닝)과의 연계
Low-latency C++ Language Optimization 트랙(Tr.02)에서는 인라이닝 유도를 다룹니다. 인라이닝이 되려면 컴파일러가 호출 지점에서 함수 정의를 볼 수 있어야 하고 최적화 레벨이 -O1 이상이어야 하며, -O0에서는 인라이닝이 거의 켜지지 않습니다. 따라서 “인라이닝을 유도하는 코드 배치·final·LTO"는 -O2 또는 -O3와 함께 쓸 때 효과가 납니다. 이 챕터는 “어떤 레벨을 켤지"를, Tr.02은 “그 레벨에서 인라이닝이 실제로 일어나도록 코드를 어떻게 쓸지"를 다룹니다.
학습 성과 목표와 평가 기준
이 글을 읽은 후 다음을 할 수 있어야 합니다. -O0~-Ofast와 MSVC /O1·/O2·/Ox의 의미·권장 용도를 설명하고, -O2 vs -O3 trade-off(속도·코드 크기·컴파일 시간·회귀)를 근거로 레벨을 선택하며, -Ofast의 부동소수점 위험을 들어 언제 피할지 판단하고, 릴리즈/디버그/프로파일 빌드 전략을 설계해 CI에서 회귀를 측정할 수 있습니다. 아래 항목으로 스스로 점검해 봅니다.
- -O2와 -O3를 선택할 때 고려할 네 요소(속도·코드 크기·컴파일 시간·회귀)를 나열할 수 있는가?
- -Ofast를 써도 되는 경우와 피해야 하는 경우를 구분할 수 있는가?
- 디버그와 릴리즈에 서로 다른 레벨을 적용하는 이유를 설명할 수 있는가?
- 동일 벤치마크로 -O2 vs -O3를 비교하는 절차를 단계별로 말할 수 있는가?
- CI에서 성능 회귀를 감지하려면 어떤 빌드·실행·비교가 필요한지 말할 수 있는가?
- MSVC의 /O1·/O2·/Ox가 GCC/Clang의 어떤 레벨에 대응하는지 설명할 수 있는가?
용어 정리
| 용어 | 설명 |
|---|---|
| -O0~-O3 | GCC/Clang 최적화 레벨; 숫자가 클수록 더 많은·공격적인 최적화 적용 |
| -Os | 크기 최소화 방향 최적화; 인라이닝·언롤링 억제 |
| -Ofast | -O3 + 부동소수점 표준 완화(-ffast-math 계열) |
| IR | Intermediate Representation; 컴파일러 내부 중간 표현 |
| I-cache | Instruction cache; 명령어 캐시; 코드 크기가 크면 미스 증가 가능 |
| 회귀 | 최적화 레벨을 올렸을 때 특정 코드에서 성능이 나빠지는 현상 |
| RelWithDebInfo | Release with Debug Info; -O2 수준 + 디버그 심볼 |
참고 자료
- GCC: Optimize Options — -O0~-O3, -Os, -Ofast 설명.
- Clang: Optimization Levels — 동일 레벨 이름의 동작.
- MSVC: /O Options — /O1, /O2, /Ox.
핵심 요약
| 항목 | 요약 |
|---|---|
| 기본 권장 | 일반 릴리즈는 -O2; 수치·벡터화 핫패스는 -O3를 벤치 검증 후 고려 |
| -Ofast | 부동소수점 표준 완화; 금융·규제·재현성 중요 시 사용 금지 |
| 검증 | 동일 벤치마크로 -O2 vs -O3 비교, 회귀 없을 때만 -O3 채택 |
| 디버그 | -O0 또는 -O1 + -g |
다음 장에서는
**LTO(Link-Time Optimization)**와 ThinLTO 개념, GCC/Clang/MSVC에서의 활성화 방법, LTO on/off 성능·크기 검증을 다룹니다.
→ LTO와 ThinLTO 실전 적용 (챕터 02)
![Featured image of post [Compiler 03] 최적화 플래그: -O2/-O3/-Ofast](/post/compiler-optimization/optimization-flags-o2-o3-ofast/wordcloud_hu_49e552ed75045497.webp)
![[Compiler 03] Introduction: Low-latency 컴파일러·빌드 최적화](/post/compiler-optimization/getting-started-compiler-build-performance-tuning/wordcloud_hu_54fcb9b6f9bee677.webp)
![[Compiler 03] 최적화 플래그: -O2/-O3/-Ofast](/post/compiler-optimization/optimization-flags-o2-o3-ofast/wordcloud_hu_7e53824b01fed297.webp)
![[Compiler 03] LTO와 ThinLTO 실전 적용](/post/compiler-optimization/lto-thinlto-practical/wordcloud_hu_660aa372ab0415ad.webp)
![[Compiler 03] PGO 고급 워크플로우](/post/compiler-optimization/pgo-workflow-advanced/wordcloud_hu_d830eb9ddd44a5a6.webp)
![[Compiler 03] GCC vs Clang vs MSVC 최적화 차이](/post/compiler-optimization/compiler-comparison-gcc-clang-msvc/wordcloud_hu_f9e92dca98d719c3.webp)
![[Compiler 03] AutoFDO 워크플로우: 샘플링 기반 프로파일 최적화](/post/compiler-optimization/autofdo-workflow-sampling-based/wordcloud_hu_d305107584078a19.webp)
![[Compiler 03] C++20 Modules와 빌드·성능](/post/compiler-optimization/cpp20-modules-build-performance/wordcloud_hu_fb6a0090a25cd0b2.webp)
![[Compiler 03] CPU 기능별 함수 다중 버전](/post/compiler-optimization/function-multiversioning-cpu/wordcloud_hu_f5001b04a04f9a62.webp)