Go 프로파일링(pprof 사용)

발행: (2025년 12월 15일 오전 11:07 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

pprof란?

pprof는 Go에 내장된 프로파일링 도구로, 애플리케이션의 런타임 데이터를 수집하고 분석할 수 있습니다. CPU 사용량, 메모리 할당, goroutine, 블로킹 연산 등 다양한 정보를 확인할 수 있습니다.

쉽게 말해, pprof는 다음과 같은 질문에 답합니다:

  • 왜 내 애플리케이션이 느린가?
  • CPU 시간이 어디에 사용되고 있는가?
  • 어떤 부분이 메모리를 많이 할당하고 있는가?
  • goroutine이 새어나가고 있는가?

스포일러: pprof는 코드를 마법처럼 최적화해 주지는 않습니다. 어디서 실수했는지 알려줄 뿐입니다.

pprof 작동 방식

pprof는 두 단계로 동작합니다:

  1. 수집 – 런타임 동안 애플리케이션에서 메트릭을 모읍니다.
  2. 분석go tool pprof를 사용해 수집된 메트릭을 살펴봅니다.

런타임에 Go는 실행을 샘플링하고 메트릭을 기록합니다. 이 샘플들은 프로파일로 집계되어 pprof 명령으로 시각화하고 탐색할 수 있습니다. 내부적으로 Go는 통계적 프로파일링을 수행합니다: 프로그램을 주기적으로 중단하고 그 순간 실행 중이던 코드를 기록합니다. 시간이 지나면서 이러한 스냅샷이 리소스 사용량에 대한 정확한 그림을 만들어 줍니다.

프로파일링을 간단히 활성화하려면 내장 HTTP 서버를 노출하면 됩니다:

main.go

이렇게 하면 /debug/pprof/ 아래에 프로파일링 엔드포인트가 노출됩니다:

  • /debug/pprof/profile – CPU
  • /debug/pprof/heap – 메모리
  • …그 외 여러 엔드포인트

프로파일 수집

/debug/pprof/profile 같은 pprof 엔드포인트에 접근하면, 응답은 바이너리 protobuf(보통 gzip 압축) 형태이며, 원시 샘플 데이터, 스택 트레이스, 카운터, 타임스탬프 등을 포함합니다. 이는 인간이 읽을 수 있는 보고서가 아닙니다.

pprof는 이 원시 데이터를 디코딩하고, 집계하여 읽기 쉬운 형태로 보여주는 도구입니다.

프로파일 분석

먼저 pprof CLI를 설치합니다:

go install github.com/google/pprof@latest

$GOPATH/bin에 바이너리가 설치된 것을 확인할 수 있습니다.

다음으로 웹 서버를 실행한 상태에서 CPU 프로파일을 10초 동안 가져옵니다:

go tool pprof http://localhost:8080/debug/pprof/profile?seconds=10

설정이 올바르게 되어 있으면, pprof 인터랙티브 쉘에 들어가 top, list, web 등 다양한 명령을 실행할 수 있습니다(전체 명령 목록은 여기 참고).

The Big Bad Boy

CPU를 많이 잡아먹는 엔드포인트가 있다고 가정해봅시다, 예를 들어 /work.

부하를 걸어봅니다:

curl http://localhost:8080/work

이 작업을 반복하거나 hey 혹은 ab 같은 부하 생성기를 사용할 수 있습니다.

실행 중인 서버에서 10초짜리 CPU 프로파일을 수집합니다:

go tool pprof http://localhost:6060/debug/pprof/profile?seconds=10

pprof 쉘 안에서 CPU를 가장 많이 사용하는 함수들을 확인하려면:

(pprof) top

예시 출력:

Showing nodes accounting for 7470ms, 92.45% of 8080ms total
Dropped 2 nodes (cum <= 40.40ms)
Showing top 10 nodes out of 35
      flat  flat%   sum%        cum   cum%
    2030ms 25.12% 25.12%     2030ms 25.12%  math.archExp
    1570ms 19.43% 44.55%     1570ms 19.43%  math.IsInf (inline)
    1500ms 18.56% 63.12%     2830ms 35.02%  math.log
     560ms  6.93% 70.05%     5800ms 71.78%  math.pow
     430ms  5.32% 75.37%     1050ms 13.00%  math.sin
     330ms  4.08% 79.46%      330ms  4.08%  runtime.pthread_cond_signal
     320ms  3.96% 83.42%     1560ms 19.31%  math.frexp
     290ms  3.59% 87.00%      290ms  3.59%  math.Float64frombits (inline)
     220ms  2.72% 89.73%      220ms  2.72%  math.IsNaN (inline)
     220ms  2.72% 92.45%      230ms  2.85%  math.normalize (inline)

누적 비용을 보려면:

(pprof) top -cum

출력 일부:

Showing nodes accounting for 2170ms, 26.86% of 8080ms total
Dropped 2 nodes (cum <= 40.40ms)
Showing top 10 nodes out of 35
      flat  flat%   sum%        cum   cum%
     110ms  1.36%  1.36%     7750ms 95.92%  main.heavyComputation
         0     0%  1.36%     7750ms 95.92%  main.main.func1
         0     0%  1.36%     7750ms 95.92%  net/http.(*ServeMux).ServeHTTP
         0     0%  1.36%     7750ms 95.92%  net/http.(*conn).serve
         0     0%  1.36%     7750ms 95.92%  net/http.HandlerFunc.ServeHTTP
         0     0%  1.36%     7750ms 95.92%  net/http.serverHandler.ServeHTTP
         0     0%  8.29%     2830ms 35.02%  math.Log (inline)

main.heavyComputation 함수가 대부분의 CPU 사용량을 차지하고 있습니다—우리의 “Big Bad Boy”.

다른 측면 프로파일링

메모리, 블록, 뮤텍스, goroutine 프로파일도 같은 방식으로 수집하고 분석할 수 있습니다.

메모리 (heap)

go tool pprof http://localhost:6060/debug/pprof/heap

이 명령을 통해 할당을 탐색하고, 메모리 누수를 식별하며, 어떤 함수가 가장 많은 메모리를 차지하는지 확인할 수 있습니다. top, list, web 등 CPU 프로파일에서 사용한 명령들을 힙 프로파일에도 동일하게 적용할 수 있습니다.

Back to Blog

관련 글

더 보기 »

Go 서버에서 고성능 SQLite 읽기

워크로드 가정 이 권장 사항은 다음을 전제로 합니다: - 읽기가 쓰기보다 우세하고 쓰기는 드물거나 오프라인 - 단일 서버 프로세스가 데이터베이스를 소유함 - 다중 goroutine…