성능 최적화는 추측이 아니라 측정에서 시작한다. 전체 실행 시간, 함수별 누적 시간, 메모리 할당을 서로 다른 도구로 본다.

from timeit import timeit

elapsed = timeit(
    "sum(range(1000))",
    number=10_000,
)
print(elapsed)

`timeit`은 워밍업과 반복을 다루는 작은 조각 비교에 맞다. 실제 애플리케이션은 `cProfile`과 샘플링 프로파일러로 병목의 호출 관계를 본다. 평균만 보지 말고 입력 크기와 지연 시간 분포를 남긴다.

Python 루프보다 내장 `sum`, `min`, `join`과 C로 구현된 라이브러리가 빠른 경우가 많다. 데이터 구조를 set으로 바꾸어 선형 검색을 평균 상수 검색으로 만드는 변화가 더 크다.

캐시는 계산을 줄이는 대신 메모리와 오래된 값 문제를 만든다. `lru_cache`의 키는 해시 가능해야 하고, 크기 제한과 무효화 조건을 정한다. CPU 병목이 남으면 NumPy, 확장 모듈, 프로세스 병렬화를 순서대로 검토한다.

Python 29. 프로파일링과 성능 최적화의 핵심 흐름도
그림. 성능 개선은 측정된 병목을 한 번에 하나씩 줄이고 다시 확인한다.