ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] 파이썬 프로파일링: 성능 병목 현상 찾는 방법
    언어/파이썬 & 장고 2026. 4. 7. 23:25

    📌 이 문서는 Real Python - Profiling in Python 및 추가 웹 검색을 바탕으로 정리되었습니다.


    개요

    소프트웨어 프로파일링이란 실행 중인 프로그램의 다양한 메트릭을 수집·분석하여 성능 병목 지점을 찾는 과정입니다.

    주요 병목 원인:

    • 과도한 메모리 사용
    • 비효율적인 CPU 활용
    • 부최적의 데이터 배치로 인한 캐시 미스

    "Make it work, then make it beautiful, then if you really, really have to, make it fast."


    언제 최적화할 것인가?

    최적화는 반드시 다음 순서로 진행해야 합니다:

    1. Testing — 코드가 올바르게 작동하는지 검증
    2. Refactoring — 유지보수성과 파이썬스러움 개선
    3. Profiling — 비효율적인 부분 식별 후 최적화

    ⚠️ 2024년 개발자 조사에 따르면, 파이썬 개발자의 47%가 프로파일링 없이 최적화를 시도하며, 그 중 2/3는 아무 효과가 없거나 오히려 성능이 악화되었습니다.


    주요 프로파일링 도구

    1. time 모듈 — 기본 측정

    📦 분류: Python 표준 라이브러리 — 별도 설치 없이 바로 사용 가능합니다.

    import time
    
    t1 = time.perf_counter()
    function()
    t2 = time.perf_counter()
    print(f"실행 시간: {t2 - t1:.2f}초")
    • 실제 경과 시간(wall-clock time)과 CPU 시간 측정
    • I/O 바운드와 CPU 바운드 작업 구분 가능
    • 설정이 간단하지만 외부 노이즈 영향을 받음

    2. timeit 모듈 — 정확한 벤치마킹

    📦 분류: Python 표준 라이브러리 — 별도 설치 없이 바로 사용 가능합니다.

    from timeit import timeit
    
    total_time = timeit("fib(30)", number=100, globals=globals())
    average = total_time / 100
    • 가비지 컬렉션을 비활성화한 상태로 반복 실행
    • 시스템 노이즈 최소화
    • 짧은 코드 스니펫 벤치마킹에 최적

    3. cProfile — 결정론적 프로파일러

    📦 분류: Python 표준 라이브러리cProfilepstats 모두 내장 모듈로, 별도 설치 없이 바로 사용 가능합니다.

    from cProfile import Profile
    from pstats import Stats, SortKey
    
    with Profile() as profile:
        fib(35)
        Stats(profile).strip_dirs().sort_stats(SortKey.CALLS).print_stats()

    수집 정보:

    • 함수 호출 횟수
    • 함수별 실행 시간
    • 재귀 호출 통계
    장점 단점
    모든 함수 호출 추적으로 정확함 높은 오버헤드 (약 15.4%)
    표준 라이브러리 내장 결과가 복잡하고 해석이 어려울 수 있음

    4. Pyinstrument — 통계적 프로파일러

    📦 분류: 외부 라이브러리 — pip로 설치 후 import하여 사용합니다.

    pip install pyinstrument
    from pyinstrument import Profiler
    from pyinstrument import Profiler
    
    with Profiler(interval=0.1) as profiler:
        estimate_pi(n=10_000_000)
    
    profiler.print()
    profiler.open_in_browser()  # 인터랙티브 웹 보고서
    • 일정한 간격으로 콜 스택 스냅샷 촬영
    • 낮은 오버헤드 (간격 조정 가능)
    • 시각적 계층 구조로 결과 표시
    • 웹 브라우저에서 인터랙티브 보고서 확인 가능
    • ⚠️ 멀티스레드와 C 확장 모듈 분석에 제약 있음

    5. Linux perf — 고급 하드웨어 프로파일러 (Python 3.12+)

    📦 분류: Linux 시스템 도구 — pip 패키지가 아닙니다. OS 패키지 매니저로 설치하며, Python 코드에 import가 필요 없습니다.

    # Ubuntu/Debian
    sudo apt-get install linux-tools-common linux-tools-generic
    
    # CentOS/RHEL
    sudo yum install perf
    sudo perf record -g -F 999 \
        "$HOME/python-custom-build/bin/python3" -X perf script.py
    
    sudo perf report --hierarchy --sort comm,dso,sample
    • 하드웨어 성능 카운터 접근
    • CPU 사이클, 캐시 미스, 메모리 대역폭 측정
    • C 라이브러리와 Linux 커널 수준 분석
    • 최소 오버헤드

    병목 현상을 쉽게 찾는 방법 (추가 도구)

    line_profiler — 라인별 CPU 분석 (가장 직관적)

    📦 분류: 외부 라이브러리 — pip로 설치 필요. @profile 데코레이터는 kernprof로 실행 시 자동으로 주입되므로 별도 import가 불필요합니다.

    pip install line-profiler
    # @profile 데코레이터로 분석할 함수 지정
    @profile
    def slow_function():
        result = []
        for i in range(10000):
            result.append(i ** 2)
        return result
    kernprof -l -v script.py
    • 각 줄의 실행 시간을 상세히 표시
    • 어느 라인이 병목인지 바로 확인 가능
    • CPU 병목 원인 찾기에 가장 직관적인 도구

    py-spy — 프로덕션 환경용 프로파일러

    📦 분류: 외부 라이브러리 (CLI 도구) — pip로 설치 후 터미널에서 직접 실행합니다. Python 코드에 import가 필요 없습니다.

    pip install py-spy
    py-spy record -o profile.svg -- python script.py
    py-spy top -- pid 12345  # 실행 중인 프로세스 실시간 분석
    • 오버헤드 단 2.1% (cProfile 대비 약 7배 낮음)
    • 실행 중인 Python 프로세스에 attach 가능
    • 스레드 분석 지원
    • Flame Graph 출력

    Scalene — CPU + 메모리 통합 분석

    📦 분류: 외부 라이브러리 (CLI 도구) — pip로 설치 후 터미널에서 직접 실행합니다. Python 코드에 import가 필요 없습니다.

    pip install scalene
    scalene script.py
    • CPU, GPU, 메모리 사용량을 라인별로 동시 측정
    • Python 코드와 네이티브 C 코드 구분 분석
    • 2023년 이후 가장 주목받는 프로파일러 중 하나
    • 메모리 누수 탐지에도 효과적

    memory_profiler — 메모리 사용량 분석

    📦 분류: 외부 라이브러리 — 설치: pip install memory-profiler / import: from memory_profiler import profile

    @profile
    def memory_heavy_function():
        data = [i for i in range(1000000)]
        return data
    • 라인별 메모리 사용량 추적
    • 메모리 누수 탐지

    VizTracer — 시각화 중심 프로파일러

    📦 분류: 외부 라이브러리 (CLI 도구) — pip로 설치 후 터미널에서 직접 실행합니다. Python 코드에 import가 필요 없습니다.

    pip install viztracer
    viztracer script.py
    vizviewer result.json
    • 직관적인 결과 분석 UI
    • gprof2dot를 통한 시각화로 병목 지점 한눈에 파악
    • 타임라인 기반으로 실행 흐름 분석

    프로파일러 비교표

    도구 방식 오버헤드 정확도 사용 난이도 주요 용도
    time 타이머 낮음 중간 쉬움 간단한 측정
    timeit 타이머 낮음 높음 쉬움 코드 스니펫 벤치마킹
    cProfile 결정론적 높음 (15.4%) 매우 높음 중간 전체 함수 호출 분석
    Pyinstrument 통계적 중간 중간 중간 콜 스택 시각화
    line_profiler 결정론적 중간 높음 쉬움 라인별 CPU 분석
    py-spy 통계적 매우 낮음 (2.1%) 높음 중간 프로덕션 환경
    Scalene 통계적 낮음 높음 쉬움 CPU + 메모리 통합
    memory_profiler 결정론적 높음 높음 쉬움 메모리 누수 탐지
    perf 통계적 매우 낮음 높음 어려움 하드웨어 레벨 분석

    병목 현상 카테고리

    Python 성능 이슈는 주로 다음 5가지 범주에 속합니다:

    카테고리 비중 해결 방법
    CPU 바운드 연산 35% 알고리즘 개선, NumPy, Cython
    I/O 대기 시간 25% 비동기 처리 (asyncio)
    메모리 할당 20% 제너레이터, 메모리 풀
    비효율적인 알고리즘 15% 자료구조 및 알고리즘 교체
    외부 의존성 5% 캐싱, 배치 처리

    실전 병목 찾기 프로세스

    단계별 접근법

    1. 1단계: cProfile로 전체 개요 파악
      • 어떤 함수가 가장 많이 호출되는지 확인
      • 전체 실행 시간의 80%를 차지하는 함수 식별
    2. 2단계: line_profiler로 해당 함수 라인별 분석
      • 정확히 어느 라인이 느린지 파악
    3. 3단계: 최적화 적용
      • 메모이제이션, 알고리즘 교체, 데이터 구조 변경 등
    4. 4단계: 검증
      • timeit으로 개선 전후 비교

    파레토 원칙 활용

    코드의 20%를 최적화하면 성능의 80%를 향상시킬 수 있습니다.


    실전 예제: 피보나치 최적화

    비효율적인 코드

    def fib(n):
        return n if n < 2 else fib(n - 2) + fib(n - 1)
    
    # fib(35) 계산: 약 29,860,712 함수 호출 → 9.6초

    문제: 동일한 값을 반복 계산하는 중복 호출 발생

    최적화: 메모이제이션 적용

    from functools import lru_cache
    
    @lru_cache(maxsize=None)
    def fib(n):
        return n if n < 2 else fib(n - 2) + fib(n - 1)
    
    # fib(35) 계산: 69번 호출 → 0.0001초 미만

    도구 선택 가이드

    • 빠른 확인이 필요할 때time, timeit
    • 전체 함수 호출 분석cProfile
    • 라인별 CPU 병목 찾기line_profiler ⭐ 가장 직관적
    • 메모리 누수 탐지memory_profiler, Scalene
    • 프로덕션 환경 (낮은 오버헤드)py-spy, Pyinstrument
    • CPU + 메모리 동시 분석Scalene ⭐ 2025년 추천
    • 시각화가 필요할 때VizTracer, Pyinstrument
    • 하드웨어 레벨 분석perf (Python 3.12+, Linux)

    참고 자료

    댓글