C++ Profiling | "성능 프로파일링" 가이드

C++ Profiling | "성능 프로파일링" 가이드

이 글의 핵심

C++ Profiling에 대한 실전 가이드입니다. 개념부터 실무 활용까지 예제와 함께 상세히 설명합니다.

프로파일링이란?

프로그램 성능을 측정하고 병목 지점을 찾는 과정

// 측정 전: 어디가 느린지 모름
void process() {
    step1();
    step2();
    step3();
}

// 측정 후: step2가 90% 시간 소요

기본 시간 측정

#include <chrono>
#include <iostream>

void measureTime() {
    auto start = std::chrono::high_resolution_clock::now();
    
    // 측정할 코드
    for (int i = 0; i < 1000000; i++) {
        // 작업
    }
    
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    std::cout << "시간: " << duration.count() << "ms" << std::endl;
}

실전 예시

예시 1: 함수 프로파일링

#include <chrono>
#include <iostream>

class Timer {
    std::chrono::time_point<std::chrono::high_resolution_clock> start;
    std::string name;
    
public:
    Timer(const std::string& n) : name(n) {
        start = std::chrono::high_resolution_clock::now();
    }
    
    ~Timer() {
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        std::cout << name << ": " << duration.count() << "μs" << std::endl;
    }
};

void slowFunction() {
    Timer t("slowFunction");
    // 작업
}

void fastFunction() {
    Timer t("fastFunction");
    // 작업
}

예시 2: gprof 사용

# 컴파일
g++ -pg program.cpp -o program

# 실행
./program

# 프로파일 생성
gprof program gprof.out > analysis.txt

예시 3: perf 사용

# 프로파일링
perf record ./program

# 결과 확인
perf report

# CPU 이벤트
perf stat ./program

예시 4: Valgrind Callgrind

# 프로파일링
valgrind --tool=callgrind ./program

# 결과 확인
kcachegrind callgrind.out.*

병목 지점 찾기

#include <map>
#include <chrono>

class Profiler {
    struct Entry {
        size_t count = 0;
        long long totalTime = 0;
    };
    
    std::map<std::string, Entry> entries;
    
public:
    void start(const std::string& name) {
        // 시작 시간 기록
    }
    
    void end(const std::string& name) {
        // 종료 시간 기록
    }
    
    void report() {
        for (const auto& [name, entry] : entries) {
            std::cout << name << ": " 
                      << entry.totalTime / entry.count << "μs" 
                      << " (" << entry.count << " calls)" << std::endl;
        }
    }
};

자주 발생하는 문제

문제 1: 측정 오버헤드

// ❌ 너무 자주 측정
for (int i = 0; i < 1000000; i++) {
    auto start = std::chrono::high_resolution_clock::now();
    doWork();
    auto end = std::chrono::high_resolution_clock::now();
}

// ✅ 전체 측정
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000000; i++) {
    doWork();
}
auto end = std::chrono::high_resolution_clock::now();

문제 2: 최적화 비활성화

// 디버그 빌드로 프로파일링 (느림)
g++ -g program.cpp

// ✅ 릴리스 빌드
g++ -O2 -g program.cpp

문제 3: 캐시 효과

// 첫 실행은 느림 (캐시 미스)
// 이후 실행은 빠름 (캐시 히트)

// ✅ 여러 번 실행 후 평균

문제 4: 조기 최적화

// ❌ 측정 전 최적화
void func() {
    // 복잡한 최적화
}

// ✅ 측정 후 최적화
// 1. 프로파일링
// 2. 병목 지점 확인
// 3. 해당 부분만 최적화

프로파일링 도구

# gprof
g++ -pg program.cpp
./a.out
gprof a.out gmon.out

# perf (Linux)
perf record ./program
perf report

# Valgrind Callgrind
valgrind --tool=callgrind ./program

# Instruments (Mac)
instruments -t "Time Profiler" ./program

# Visual Studio Profiler (Windows)

FAQ

Q1: 프로파일링은 언제?

A:

  • 성능 문제 발생
  • 최적화 전
  • 정기적 모니터링

Q2: 어떤 도구?

A:

  • gprof: 기본
  • perf: Linux 상세
  • Valgrind: 정확
  • Instruments: Mac

Q3: 측정 단위는?

A:

  • 마이크로초 (μs)
  • 밀리초 (ms)
  • CPU 사이클

Q4: 최적화 순서는?

A:

  1. 측정
  2. 병목 찾기
  3. 최적화
  4. 재측정

Q5: 프로덕션 프로파일링?

A:

  • 샘플링 프로파일러
  • 낮은 오버헤드
  • 통계 수집

Q6: 프로파일링 학습 리소스는?

A:

  • “Optimized C++”
  • perf 문서
  • Valgrind 문서

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • C++ Exception Performance | “예외 성능” 가이드
  • C++ Cache Optimization | “캐시 최적화” 가이드
  • C++ 프로파일링 | “어디가 느린지 모르겠어요” perf·gprof로 병목 찾기

관련 글

  • C++ Cache Optimization |
  • C++ Exception Performance |
  • C++ Algorithm Sort |
  • C++ Benchmarking |
  • C++ Branch Prediction |