C++ 컴파일러 뭘 쓸까? GCC vs Clang vs MSVC 차이·선택 가이드
이 글의 핵심
C++ 컴파일러 선택 가이드: GCC, Clang, MSVC 비교(msvc vs clang 포함). 각 컴파일러 특징·성능·에러 메시지 차이, 실무에서 언제 어떤 걸 쓸지, PGO·LTO 고급 최적화까지 3부작으로 정리.
[C++ 실전 가이드 #2] C++ 컴파일러 완벽 가이드
이 글을 읽으면 GCC·Clang·MSVC의 차이를 완전히 이해하고, 프로젝트에 맞는 컴파일러를 선택할 수 있습니다. 같은 C++ 코드라도 컴파일러에 따라 실행 속도가 최대 30% 차이날 수 있으므로, 이 가이드의 비교표와 벤치마크를 참고해 결정하세요.
어떤 컴파일러를 선택해야 할까요? (문제 시나리오)
새 C++ 프로젝트를 시작할 때, 또는 기존 프로젝트의 성능을 개선하려 할 때 “GCC, Clang, MSVC 중 어떤 걸 써야 하지?”라는 질문을 자주 받습니다.
실무에서 겪는 전형적인 상황
상황 1: Linux 서버용 백엔드 개발
- 팀원 A: “우리 서버는 Ubuntu인데 GCC 쓰면 되죠?”
- 팀원 B: “Clang이 에러 메시지가 더 좋다던데요?”
- 고민: 표준 라이브러리 차이, 배포 환경 일치 여부
상황 2: 크로스플랫폼 게임 엔진
- Windows(MSVC), macOS(Clang), Linux(GCC) 각각 빌드해야 함
- 고민: 플랫폼별 최적화, ABI 호환성, 빌드 스크립트 통일
상황 3: 성능이 중요한 수치 계산 라이브러리
-O2로 빌드했는데 벤치마크에서 기대만큼 안 나옴- 고민: GCC
-O3 -march=nativevs Clang PGO vs MSVC LTCG, 어떤 조합이 최선인가?
상황 4: 링크 에러·ABI 충돌
- “GCC로 컴파일한 .so와 Clang으로 컴파일한 .a를 함께 링크하면 undefined reference”
- 고민: 표준 라이브러리 ABI 불일치, 해결 방법
상황 5: 템플릿 에러로 디버깅 지옥
- 복잡한 템플릿 코드에서 50줄짜리 에러 메시지가 쏟아짐
- 고민: 어떤 컴파일러가 에러 메시지를 더 읽기 쉽게 출력하는가?
상황 6: Docker 기반 CI/CD 파이프라인
- 멀티 아키텍처(amd64, arm64) 빌드 필요
- 고민: GCC vs Clang 크로스 컴파일, 빌드 시간 최소화
이 글에서는 위와 같은 상황에서 어떤 결정을 내려야 하는지 구체적인 비교표, 벤치마크, 실무 권장사항으로 정리합니다.
목차
- 문제 시나리오
- 동일 코드 3컴파일러 비교 예제
- GCC vs Clang vs MSVC 상세 비교
- 최적화 플래그와 성능
- C++ 표준 지원 비교
- 플랫폼별 특성
- 자주 발생하는 에러와 해결법
- 성능 벤치마크
- 프로덕션 패턴과 권장사항
- 시리즈 구성 및 연관 글
문제 시나리오 요약
| 상황 | 고민 포인트 | 이 글에서 찾는 답 |
|---|---|---|
| 새 프로젝트 시작 | 어떤 컴파일러를 기본으로? | 상세 비교, 플랫폼별 특성 |
| 성능 최적화 | -O2 vs -O3, PGO vs LTO? | 최적화 플래그, 벤치마크 |
| 크로스플랫폼 | Windows/MSVC, macOS/Clang, Linux/GCC | 플랫폼별 특성, 프로덕션 권장 |
| 링크 에러·ABI | undefined reference, symbol 충돌 | 자주 발생하는 에러 |
| 에러 메시지 | 템플릿 에러 가독성 | 동일 코드 비교 |
동일 코드 3컴파일러 완전 비교 예제
같은 C++ 소스 코드를 GCC, Clang, MSVC로 각각 빌드하는 실전 예제입니다. 복사해 붙여넣고 터미널에서 바로 실행해 보세요.
공통 소스 코드 (example.cpp)
// example.cpp — GCC, Clang, MSVC 모두 동일한 소스
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
int main() {
std::vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};
std::sort(nums.begin(), nums.end());
std::string msg = "Sorted: ";
for (int n : nums) {
msg += std::to_string(n) + " ";
}
std::cout << msg << "\n";
return 0;
}
GCC로 빌드 및 실행
# GCC (Linux, macOS, MinGW)
g++ -std=c++17 -O2 -Wall -Wextra -o example_gcc example.cpp
./example_gcc
# 출력: Sorted: 1 1 2 3 4 5 6 9
Clang으로 빌드 및 실행
# Clang (Linux, macOS)
clang++ -std=c++17 -O2 -Wall -Wextra -o example_clang example.cpp
./example_clang
# 출력: Sorted: 1 1 2 3 4 5 6 9
MSVC로 빌드 및 실행
REM MSVC (개발자 명령 프롬프트에서)
cl /std:c++17 /O2 /EHsc /W4 example.cpp /Fe:example_msvc.exe
example_msvc.exe
REM 출력: Sorted: 1 1 2 3 4 5 6 9
에러 메시지 비교: 의도적 오류 예제
템플릿 타입 불일치를 일으켜 각 컴파일러의 에러 메시지를 비교해 봅니다.
// error_demo.cpp — 의도적 오류
#include <vector>
#include <string>
template<typename T>
void process(const std::vector<T>& v) {
std::string s = v[0]; // T가 int면 string 변환 불가
}
int main() {
std::vector<int> v = {1, 2, 3};
process(v); // 에러: int → string 변환
return 0;
}
GCC 에러 (간결하지만 원인 파악에 충분)
error: cannot convert 'const int' to 'std::string' in initialization
std::string s = v[0];
Clang 에러 (가장 상세, 제안까지 포함)
error: no viable conversion from 'const int' to 'std::string'
std::string s = v[0];
^~~~
note: candidate function not viable: no known conversion from 'const int' to 'const std::string &'
MSVC 에러 (Windows 스타일, 경로 포함)
error C2440: 'initialization': cannot convert from 'const int' to 'std::string'
note: No user-defined-conversion operator available
정리: Clang이 에러 위치(^~~~)와 후보 함수를 함께 보여줘서 디버깅에 유리합니다. 복잡한 템플릿 에러에서는 차이가 더 커집니다.
GCC vs Clang vs MSVC 상세 비교
개요 비교표
| 항목 | GCC | Clang | MSVC |
|---|---|---|---|
| 개발사 | GNU 프로젝트 | LLVM 프로젝트 (Apple, Google 등) | Microsoft |
| 라이선스 | GPL | Apache 2.0 / BSD | 상용 (Visual Studio 포함) |
| 주요 사용처 | Linux, 임베디드, 서버 | macOS 기본, iOS, Android NDK | Windows |
| 에러 메시지 | 간결, 충분 | 매우 상세, 색상 지원 | Windows 스타일 |
| 컴파일 속도 | 보통 | 빠름 (증분 빌드 우수) | 보통 |
| 최적화 품질 | 안정적, 예측 가능 | 공격적, 모던 | Windows 특화 |
| 표준 준수 | C++23 적극 지원 | C++23 적극 지원 | C++20/23 단계적 지원 |
GCC (GNU Compiler Collection)
강점
- Linux 생태계의 사실상 표준
- x86, ARM, RISC-V, MIPS 등 광범위한 아키텍처 지원
- 35년 이상 검증된 안정성
-march=native,-flto등 고급 최적화 옵션 풍부
약점
- 에러 메시지가 Clang보다 덜 친절함
- C++ 모듈 등 최신 기능 지원이 Clang보다 느릴 수 있음
적합한 프로젝트: Linux 서버, 임베디드, HPC, 크로스플랫폼 중 Linux 타겟
# GCC 기본 사용 예
g++ -std=c++20 -O2 -Wall -Wextra -o app main.cpp
Clang (LLVM/Clang)
강점
- 에러 메시지가 매우 우수 — 템플릿 에러도 읽기 쉬움
- 컴파일 속도가 빠르고 증분 빌드 효율적
- 정적 분석 도구(Clang-Tidy, Clang Static Analyzer)와 통합
- macOS, iOS 기본 컴파일러
약점
- Linux에서 libstdc++(GCC) vs libc++(Clang) 선택 필요
- 일부 아키텍처에서 GCC보다 최적화가 약할 수 있음
적합한 프로젝트: macOS/iOS, 크로스플랫폼, 코드 품질·정적 분석 중시
# Clang 기본 사용 예
clang++ -std=c++20 -O2 -Wall -Wextra -o app main.cpp
MSVC (Microsoft Visual C++)
강점
- Windows 네이티브, Visual Studio와 완벽 통합
- Windows API, COM, DirectX 등 플랫폼 특화 최적화
- IntelliSense, 디버거, 프로파일러 통합
약점
- Windows 전용 (다른 OS에서는 clang-cl 등 대안)
- 표준 준수가 GCC/Clang보다 느린 편
적합한 프로젝트: Windows 전용 앱, 게임, 엔터프라이즈 데스크톱
REM MSVC (개발자 명령 프롬프트)
cl /std:c++20 /O2 /EHsc main.cpp
최적화 플래그와 성능
기본 최적화 수준 비교
| 수준 | GCC | Clang | MSVC | 용도 |
|---|---|---|---|---|
| 없음 | -O0 | -O0 | /Od | 디버깅 |
| 기본 | -O1 | -O1 | /O1 | 빠른 컴파일 |
| 권장 | -O2 | -O2 | /O2 | 실무 배포 표준 |
| 공격적 | -O3 | -O3 | /O2 (MSVC는 -O3 없음) | 최대 성능 |
| 크기 | -Os | -Os | /O1 | 임베디드, 작은 바이너리 |
상세 최적화 플래그 참조표
GCC 주요 플래그
# 디버그 빌드 (최적화 없음)
g++ -O0 -g -Wall main.cpp -o app_debug
# 릴리스 기본 (실무 표준)
g++ -O2 -DNDEBUG main.cpp -o app
# 최대 성능 (단일 서버/개발 PC)
g++ -O3 -march=native -DNDEBUG main.cpp -o app_fast
# LTO (링크 타임 최적화)
g++ -O3 -flto -march=native main.cpp -o app_lto
# PGO 1단계: 프로파일 수집용 빌드
g++ -O3 -fprofile-generate main.cpp -o app_pgo
# 실행 후 default.gcda 생성
# PGO 2단계: 프로파일 기반 재빌드
g++ -O3 -fprofile-use main.cpp -o app_pgo_final
Clang 주요 플래그
# 기본 릴리스
clang++ -O2 -DNDEBUG main.cpp -o app
# 최대 성능
clang++ -O3 -march=native -DNDEBUG main.cpp -o app_fast
# LTO
clang++ -O3 -flto -march=native main.cpp -o app_lto
# PGO (LLVM 프로파일)
clang++ -O3 -fprofile-instr-generate main.cpp -o app_pgo
# 실행 후 default.profraw 생성, llvm-profdata merge로 변환
clang++ -O3 -fprofile-instr-use=default.profdata main.cpp -o app_pgo_final
MSVC 주요 플래그
REM 디버그
cl /Od /Zi /EHsc main.cpp
REM 릴리스 기본
cl /O2 /DNDEBUG /EHsc main.cpp
REM 전체 프로그램 최적화 + LTCG
cl /O2 /GL /DNDEBUG main.cpp /link /LTCG
REM AVX2 명령어 활용
cl /O2 /arch:AVX2 /DNDEBUG main.cpp
REM PGO (Visual Studio 프로젝트 설정에서 주로 사용)
REM 1) /GL /LTCG 빌드 → 2) 실행 → 3) pgc 파일 생성 → 4) /LTCG:PGOPTIMIZE 재빌드
고급 최적화 옵션
GCC
g++ -O3 -march=native -flto main.cpp -o app
# -march=native: 현재 CPU 최대 활용
# -flto: 링크 타임 최적화
Clang
clang++ -O3 -march=native -flto main.cpp -o app
# PGO: 1) -fprofile-instr-generate 빌드 → 실행 → 2) -fprofile-instr-use 재빌드
MSVC
cl /O2 /GL /arch:AVX2 main.cpp /link /LTCG
REM /GL: 전체 프로그램 최적화, /LTCG: 링크 타임 코드 생성
최적화 플래그 효과 요약
| 기법 | 예상 성능 향상 | 컴파일/링크 시간 | 주의사항 |
|---|---|---|---|
-O2 → -O3 | 0~15% | +10~30% | 일부 코드는 -O2가 더 나을 수 있음 |
-march=native | 5~15% | 거의 동일 | 다른 CPU에서 실행 불가 |
| LTO / LTCG | 5~15% | 링크 시간 크게 증가 | 메모리 사용량 증가 |
| PGO | 10~30% | 2회 빌드 필요 | 프로파일 수집 단계 필요 |
플래그별 상세 설명
| 플래그 | GCC | Clang | MSVC | 설명 |
|---|---|---|---|---|
| 루프 언롤링 | -funroll-loops (O3 포함) | -funroll-loops | /Ob2 | 작은 루프 펼치기 |
| 인라인 한계 | -finline-limit=N | -inline-threshold=N | /Ob2 | 인라인 공격성 |
| 벡터화 보고 | -fopt-info-vec | -Rpass=loop-vectorize | /Qvec-report | 벡터화 여부 확인 |
| 경고 레벨 | -Wall -Wextra | 동일 | /W4 | 실무 권장 |
C++ 표준 지원 비교
C++ 표준별 지원 현황 (2026년 기준)
| 표준 | GCC | Clang | MSVC |
|---|---|---|---|
| C++98 | ✅ 완전 | ✅ 완전 | ✅ 완전 |
| C++11 | ✅ 완전 | ✅ 완전 | ✅ 완전 |
| C++14 | ✅ 완전 | ✅ 완전 | ✅ 완전 |
| C++17 | ✅ 완전 | ✅ 완전 | ✅ 완전 |
| C++20 | ✅ 완전 | ✅ 완전 | ✅ 대부분 (모듈 등 일부 진행 중) |
| C++23 | ✅ 대부분 | ✅ 대부분 | 🔄 단계적 지원 |
표준 활성화 방법
# GCC / Clang
g++ -std=c++20 main.cpp
clang++ -std=c++23 main.cpp
REM MSVC
cl /std:c++20 main.cpp
cl /std:c++latest main.cpp
REM 최신 실험적 기능
표준 라이브러리 구현 차이
| 구현 | 사용처 | SSO 임계값 | 비고 |
|---|---|---|---|
| libstdc++ | GCC 기본 | ~15 bytes | Linux 표준 |
| libc++ | Clang 기본 (macOS) | ~22 bytes | LLVM 프로젝트 |
| MSVC STL | MSVC | 구현 의존 | Windows 전용 |
SSO(Small String Optimization): 짧은 문자열은 힙 할당 없이 객체 내부에 저장. 임계값 차이로 std::string 크기·성능이 컴파일러마다 다를 수 있음.
플랫폼별 특성
OS별 권장 컴파일러
flowchart TD
A[프로젝트 타겟] --> B{OS?}
B -->|Linux| C[GCC 권장]
B -->|macOS/iOS| D[Clang 권장]
B -->|Windows| E[MSVC 권장]
B -->|크로스플랫폼| F[각 OS별 해당 컴파일러]
C --> G[대안: Clang + libstdc++]
D --> H[대안: 없음 - Clang이 기본]
E --> I[대안: clang-cl]
플랫폼 특화 기능
GCC (Linux)
__attribute__((visibility("hidden")))— 심볼 노출 제어-static-libgcc,-static-libstdc++— 정적 링크- 다양한
-march값 (x86-64-v2, x86-64-v3, armv8-a 등)
Clang (macOS)
- Apple Silicon (M1/M2) 네이티브 지원
- Xcode 통합, Instruments 프로파일링
-stdlib=libc++(기본)
MSVC (Windows)
__declspec(dllexport/dllimport)— DLL 내보내기/MD,/MT— 런타임 링크 방식- Windows SDK, DirectX 헤더와 완벽 호환
크로스 컴파일 예시
# GCC로 ARM 크로스 컴파일
aarch64-linux-gnu-g++ -std=c++17 -O2 -o app_arm main.cpp
# Clang으로 Windows 타겟 (MinGW 연동)
clang++ -target x86_64-pc-windows-gnu -std=c++17 -O2 -o app.exe main.cpp
자주 발생하는 에러와 해결법
1. ABI 호환성 문제 (undefined reference, symbol 충돌)
증상
undefined reference to `std::__cxx11::basic_string<char, ...>::~basic_string()'
원인: GCC로 컴파일한 라이브러리와 Clang으로 컴파일한 코드를 함께 링크할 때, std::string 등 표준 라이브러리의 ABI(Application Binary Interface)가 다름.
해결법
# 방법 1: 동일 컴파일러 사용
# 라이브러리와 앱을 모두 GCC 또는 모두 Clang으로 빌드
# 방법 2: Clang에서 GCC libstdc++ 사용 (Linux)
clang++ -stdlib=libstdc++ -std=c++17 main.cpp -L. -lmylib -o app
# 방법 3: C 인터페이스로 경계 만들기
# C++ 라이브러리를 extern "C"로 감싸 ABI 고정
// mylib.h — C 인터페이스로 ABI 고정
#ifdef __cplusplus
extern "C" {
#endif
void mylib_process(const char* input, char* output, size_t len);
#ifdef __cplusplus
}
#endif
2. 링크 순서 문제
증상
undefined reference to `foo'
원인: 링커는 심볼을 나중에 나온 오브젝트/라이브러리에서 찾음. 의존하는 쪽이 먼저 와야 함.
해결법
# ❌ 잘못된 순서 (mylib이 foo를 사용하는데 mylib이 먼저 옴)
g++ -o app main.cpp -lmylib
# ✅ 올바른 순서 (의존하는 쪽 main.cpp 먼저, 제공하는 쪽 -lmylib 나중)
g++ -o app main.cpp -lmylib
# 또는
g++ -o app main.cpp -Wl,--start-group -lmylib -lother -Wl,--end-group
3. MSVC: 런타임 라이브러리 불일치
증상
LNK2038: mismatch detected for 'RuntimeLibrary': value 'MT_StaticRelease' doesn't match value 'MD_DynamicRelease'
원인: 일부 오브젝트는 /MT(정적), 일부는 /MD(동적)로 빌드됨.
해결법
REM 프로젝트 전체에서 동일하게
cl /MD /O2 main.cpp utils.cpp
REM 또는
cl /MT /O2 main.cpp utils.cpp
| 옵션 | 의미 |
|---|---|
/MD | MSVC 런타임 동적 링크 (일반적으로 권장) |
/MT | MSVC 런타임 정적 링크 (배포 단순화, 바이너리 크기 증가) |
4. GCC/Clang: C++17 이상에서 std::filesystem 링크 에러
증상
undefined reference to `std::filesystem::exists(std::filesystem::__cxx11::path const&)'
해결법
# C++17 filesystem은 별도 라이브러리 링크 필요 (GCC 8 이전)
g++ -std=c++17 main.cpp -lstdc++fs -o app
# GCC 9+ / Clang 9+ 에서는 보통 자동 링크됨
5. LTO 사용 시 링크 에러
증상: -flto 사용 시 undefined reference 또는 plugin needed to handle lto object
해결법
# 모든 오브젝트를 동일한 -flto 옵션으로 컴파일
g++ -O3 -flto -c a.cpp -o a.o
g++ -O3 -flto -c b.cpp -o b.o
g++ -O3 -flto a.o b.o -o app
# ar로 정적 라이브러리 만들 때도 flto 일치
g++ -O3 -flto -c mylib.cpp -o mylib.o
ar rcs libmylib.a mylib.o
6. MSVC: std::min/std::max 매크로 충돌
증상
error C2589: 'min': illegal token on right side of '::'
원인: windows.h가 min/max 매크로를 정의함.
해결법
// 방법 1: 매크로 정의 전에 windows.h 포함
#define NOMINMAX
#include <windows.h>
#include <algorithm>
// 방법 2: 괄호로 감싸기
int x = (std::min)(a, b);
7. GCC/Clang: -march=native 바이너리를 다른 CPU에서 실행 시
증상
Illegal instruction (core dumped)
원인: -march=native로 빌드한 바이너리를 더 오래된 CPU에서 실행.
해결법
# 배포용: 범용 아키텍처 지정
g++ -O3 -march=x86-64-v2 main.cpp -o app
# 또는
g++ -O3 -march=x86-64-v3 main.cpp -o app
8. MSVC: UTF-8 소스 인코딩 문제
증상
warning C4819: The file contains a character that cannot be represented in the current code page
해결법
REM UTF-8로 저장된 소스 컴파일
cl /utf-8 /std:c++17 main.cpp
// 또는 소스 상단에 BOM 없이 UTF-8 선언 (MSVC 2015+)
// #pragma execution_character_set("utf-8")
9. PGO 프로파일 불일치
증상
error: profile data may be out of date
해결법: 소스 변경 후 프로파일을 다시 수집해야 함. -fprofile-use 시 default.profdata가 현재 소스와 맞는지 확인.
# Clang: 프로파일 병합 후 사용
llvm-profdata merge -output=merged.profdata *.profraw
clang++ -fprofile-instr-use=merged.profdata main.cpp -o app
10. 정적 라이브러리와 LTO
증상: ar로 만든 .a를 -flto와 함께 링크할 때 undefined reference 또는 플러그인 에러.
해결법
# GCC: ar 대신 gcc -r 사용, 또는 플러그인으로 ar 래핑
gcc-ar rcs libmylib.a mylib.o
# 또는
g++ -O3 -flto -c mylib.cpp -o mylib.o
ar rcs libmylib.a mylib.o
# 링크 시 동일 -flto
g++ -O3 -flto main.o -L. -lmylib -o app
성능 벤치마크
벤치마크 환경 (참고용)
- CPU: Intel Core i7 / Apple M1 수준
- OS: Ubuntu 22.04, macOS, Windows 11
- 컴파일러: GCC 13, Clang 17, MSVC 2022
벤치마크 1: 벡터 루프 (정수 누적)
// benchmark_vector.cpp
#include <vector>
#include <chrono>
#include <iostream>
int main() {
const size_t SIZE = 10'000'000;
std::vector<int> data(SIZE);
auto start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < SIZE; ++i) {
data[i] = static_cast<int>(i * 2);
}
auto end = std::chrono::high_resolution_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Time: " << ms << " ms\n";
return 0;
}
예상 결과 (상대값, -O2 기준 GCC=100)
| 컴파일러 | -O0 | -O2 | -O3 | -O3 -march=native |
|---|---|---|---|---|
| GCC | 350 | 100 | 85 | 75 |
| Clang | 320 | 95 | 80 | 72 |
| MSVC | 380 | 105 | — | — |
실제 수치는 하드웨어·OS에 따라 다름. 상대 비교용.
벤치마크 2: 문자열 연결 (std::string)
// benchmark_string.cpp
#include <string>
#include <chrono>
#include <iostream>
int main() {
const int ITERS = 100'000;
auto start = std::chrono::high_resolution_clock::now();
std::string s;
for (int i = 0; i < ITERS; ++i) {
s += "hello";
}
auto end = std::chrono::high_resolution_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Time: " << ms << " ms, size=" << s.size() << "\n";
return 0;
}
특징: std::string 구현이 컴파일러마다 다르므로 SSO, 할당 전략에 따라 결과 차이 발생. Clang libc++이 일부 워크로드에서 유리할 수 있음.
벤치마크 3: 행렬 곱셈 (SIMD 활용)
// benchmark_matrix.cpp — 단순 이중 루프
#include <vector>
#include <chrono>
#include <iostream>
void matmul(const std::vector<double>& A, const std::vector<double>& B,
std::vector<double>& C, int N) {
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j) {
double sum = 0;
for (int k = 0; k < N; ++k)
sum += A[i*N+k] * B[k*N+j];
C[i*N+j] = sum;
}
}
int main() {
const int N = 512;
std::vector<double> A(N*N, 1.0), B(N*N, 1.0), C(N*N, 0.0);
auto start = std::chrono::high_resolution_clock::now();
matmul(A, B, C, N);
auto end = std::chrono::high_resolution_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Time: " << ms << " ms\n";
return 0;
}
예상: -O3 -march=native에서 GCC·Clang 모두 자동 벡터화로 크게 개선. MSVC /O2 /arch:AVX2도 유사.
벤치마크 요약
| 워크로드 | GCC | Clang | MSVC | 비고 |
|---|---|---|---|---|
| 정수 루프 | 우수 | 우수 | 양호 | -O3에서 차이 |
| 문자열 처리 | 양호 | 우수 | 양호 | libc++ SSO |
| 수치 계산(SIMD) | 우수 | 우수 | 우수 | -march/arch 중요 |
| 컴파일 속도 | 보통 | 빠름 | 보통 | Clang 증분 빌드 우수 |
결론: “이 컴파일러가 무조건 빠르다”는 없음. 실제 타겟 코드로 직접 측정하는 것이 가장 확실합니다.
프로덕션 패턴과 권장사항
프로젝트 유형별 권장
| 프로젝트 유형 | 1순위 | 2순위 | 비고 |
|---|---|---|---|
| Linux 서버 | GCC | Clang | 배포 환경과 동일하게 |
| macOS/iOS | Clang | — | Xcode 기본 |
| Windows 전용 | MSVC | clang-cl | VS 통합 활용 |
| 크로스플랫폼 | 각 OS별 해당 | — | CI에서 모두 테스트 |
| 임베디드 | GCC | Clang | 아키텍처 지원 확인 |
| HPC/수치 | GCC 또는 Clang | — | -march=native, LTO 검토 |
프로덕션 체크리스트
- 배포 환경과 동일한 컴파일러로 최종 빌드 (Docker면 이미지 내 컴파일러 버전 고정)
- -march=native는 배포용에서 주의 — 타겟 CPU가 다르면 illegal instruction 가능
- ABI 일관성 — 동일 표준 라이브러리(libstdc++ vs libc++) 유지
- 경고 활용 —
-Wall -Wextra, MSVC/W4권장 - 멀티 컴파일러 CI — 가능하면 GCC + Clang (또는 MSVC) 둘 다 빌드·테스트
- 릴리스 빌드 —
-O2또는-O3+ LTO 검토, 디버그 심볼 분리(-g만 넣고 strip)
프로덕션 패턴 1: Docker 멀티 스테이지 빌드
# Dockerfile — GCC로 빌드, 최소 런타임 이미지
FROM gcc:13-bookworm AS builder
WORKDIR /app
COPY . .
RUN g++ -std=c++20 -O3 -DNDEBUG -static-libstdc++ -o app main.cpp
FROM debian:bookworm-slim
COPY --from=builder /app/app /usr/local/bin/
CMD ["app"]
프로덕션 패턴 2: GitHub Actions 멀티 컴파일러 CI
# .github/workflows/build.yml
name: Multi-Compiler Build
on: [push]
jobs:
build:
strategy:
matrix:
compiler: [gcc-13, clang-17]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install ${{ matrix.compiler }}
run: |
if [ "${{ matrix.compiler }}" = "gcc-13" ]; then
sudo apt install g++-13
echo "CXX=g++-13" >> $GITHUB_ENV
else
sudo apt install clang-17
echo "CXX=clang++-17" >> $GITHUB_ENV
fi
- name: Build
run: |
$CXX -std=c++20 -O2 -Wall -Wextra -o app main.cpp
- name: Test
run: ./app
프로덕션 패턴 3: CMake 멀티 컴파일러 지원
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(MyApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
# 최적화
if(CMAKE_BUILD_TYPE STREQUAL "Release")
add_compile_options(
$<$<CXX_COMPILER_ID:GNU,Clang>:-O3>
$<$<CXX_COMPILER_ID:MSVC>:/O2>
)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options(-march=x86-64-v2)
# 범용 x86_64
endif()
endif()
# 경고
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options(/W4)
else()
add_compile_options(-Wall -Wextra)
endif()
add_executable(app main.cpp)
프로덕션 패턴 4: Makefile 멀티 컴파일러
# Makefile
CXX ?= g++
CXXFLAGS = -std=c++20 -Wall -Wextra
RELEASE_FLAGS = -O3 -DNDEBUG
ifeq ($(CXX),clang++)
RELEASE_FLAGS += -march=x86-64-v2
else ifeq ($(CXX),g++)
RELEASE_FLAGS += -march=x86-64-v2
endif
app: main.cpp
$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) -o $@ $<
# 사용: make CXX=clang++
# 또는: make CXX=g++
배포 시 -march 선택
| 옵션 | 의미 | 용도 |
|---|---|---|
-march=x86-64 | 기본 64비트 | 최대 호환 |
-march=x86-64-v2 | SSE4.2, POPCNT 등 | 2010년대 CPU 이상 |
-march=x86-64-v3 | AVX, AVX2 | 2015년대 CPU 이상 |
-march=native | 현재 CPU 전부 | 개발/단일 서버 전용 |
릴리스 빌드 스크립트 예시
#!/bin/bash
# release_build.sh — GCC 기준
set -e
CXX=${CXX:-g++}
$CXX -std=c++20 -O3 -DNDEBUG -march=x86-64-v2 \
-Wall -Wextra -flto \
-o app_release main.cpp
strip app_release
echo "Built: app_release"
시리즈 구성 및 연관 글
이 컴파일러 가이드는 난이도별로 3개의 글로 구성되어 있습니다.
| 편 | 제목 | 링크 | 예상 시간 |
|---|---|---|---|
| 기초 | C++ 컴파일러 기초 | #2-1 | 10분 |
| 최적화 | 컴파일러 최적화 심화 | #2-2 | 12분 |
| 고급 | 컴파일러 고급 활용 | #2-3 | 15분 |
읽기 순서: 처음이라면 #2-1부터, 최적화만 필요하면 #2-2부터, 실무에서 CI·경고를 챙기려면 #2-3부터 보시면 됩니다.
flowchart LR B[#2-1 기초] --> O[#2-2 최적화] O --> A[#2-3 고급] B --> G[GCC] B --> C[Clang] B --> M[MSVC]
실행 가능 예제
컴파일러 버전 확인
g++ --version
clang++ --version
최소 C++ 예제
// hello.cpp — g++ -std=c++17 -o hello hello.cpp && ./hello
#include <iostream>
int main() {
std::cout << "Hello, C++!\n";
return 0;
}
한 줄 요약
GCC는 Linux·임베디드, Clang은 macOS·크로스플랫폼·에러 메시지, MSVC는 Windows 전용에 적합합니다. 성능은 워크로드마다 다르므로 직접 벤치마크하고, ABI·링크 순서 등 자주 나는 에러를 알아두면 실무에서 큰 도움이 됩니다.
이전 글: C++ 실전 가이드 #1: 개발 환경 구축
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. C++ 컴파일러 선택 가이드: GCC, Clang, MSVC 비교(msvc vs clang 포함). 각 컴파일러 특징·성능·에러 메시지 차이, 실무에서 언제 어떤 걸 쓸지, PGO·LTO 고급 최적화까지 3부작으로 … 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
다음 글: C++ 실전 가이드 #3: VS Code C++ 개발 환경 설정
💡 지금은 코딩부터 하고 싶다면 — #3 VS Code로 가서 빌드·디버깅부터 해도 됩니다.
전체 시리즈
- #0: C++이란? 역사·현황·용도·장단점
- #1: 개발 환경 구축
- #2: 컴파일러 완벽 가이드 (현재 글)
- #2-1: 기초편
- #2-2: 최적화편
- #2-3: 고급편
- #3: VS Code 개발 환경 설정
- #4: CMake 입문
- #5: 컴파일 과정 분석
참고 자료
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 컴파일러 비교 | GCC vs Clang vs MSVC, 어떤 걸 써야 할까?
- C++ 개발 환경 구축 | “C++ 어디서 시작하죠?” 컴파일러 설치부터 Hello World까지
- C++ 컴파일러 최적화 | PGO·LTO로 “느린 프로그램” 성능 30% 향상시키기
이 글에서 다루는 키워드 (관련 검색어)
C++, C++컴파일러, GCC, Clang, MSVC, 컴파일러가이드, 컴파일러최적화, PGO, LTO, 멀티컴파일러 등으로 검색하시면 이 글이 도움이 됩니다.
관련 글
- C++ 컴파일러 비교 | GCC vs Clang vs MSVC, 어떤 걸 써야 할까?
- C++ 컴파일러 최적화 | PGO·LTO로
- C++ 개발 환경 구축 |
- C++ 멀티 컴파일러 전략과 CI/CD 파이프라인 구축 | 실무 가이드
- VS Code C++ 설정 | IntelliSense·빌드·디버깅