Ketika program lambat atau boros memori, menebak penyebabnya dari membaca kode sama seperti menebak letak kebocoran pipa dari gambar denah rumah. Kita butuh alat ukur: profil. pprof adalah profiler bawaan Go yang merekam kejadian runtime — sampel waktu CPU, alokasi heap, jumlah goroutine — ke dalam berkas yang bisa dianalisis dengan go tool pprof. Pada chapter ini kita mengenal jenis-jenis profil yang tersedia, menulis profil CPU pertama ke berkas, lalu membaca tabel ringkasannya dengan -top.
Peta Profil yang Tersedia #
Package runtime/pprof menyediakan beberapa profil yang terpisah fungsinya. CPU profile merekam sampel stack pada interval tetap (100 Hz) selama perekaman aktif, sehingga baris teratas tabel adalah fungsi yang paling sering kedapatan sedang berjalan. Profil lain diambil sebagai snapshot melalui pprof.Lookup(nama):
| Nama profil | Yang direkam | Pertanyaan yang dijawab |
|---|---|---|
cpu | Sampel waktu CPU selama perekaman | Fungsi mana memakan waktu prosesor? |
heap | Objek yang masih hidup (inuse) | Memori dipakai siapa saat ini? |
allocs | Total alokasi kumulatif sejak mulai | Alokasi datang dari stack mana? |
goroutine | Stack semua goroutine | Goroutine macet/blocking di mana? |
threadcreate | Thread OS yang dibuat runtime | Berapa thread sistem dipakai? |
block | Waktu menunggu channel/mutex | Latensi sinkronisasi seberapa besar? |
mutex | Pemegang dan penantang mutex | Kontensi pada mutex mana? |
Menulis Profil CPU ke Berkas #
Pola dasarnya tiga langkah: buka berkas, panggil pprof.StartCPUProfile, jalankan beban kerja, tutup dengan pprof.StopCPUProfile. Berkas contoh.go di bawah halaman memakai os.CreateTemp agar contoh berjalan di mesin mana pun tanpa menimpa berkas lama:
package main
import (
"fmt"
"os"
"runtime/pprof"
)
// kerjaRingan mensimulasikan beban CPU yang hasilnya selalu sama.
func kerjaRingan() int {
total := 0
for i := range 200_000_000 {
total += i % 7
}
return total
}
func main() {
berkas, err := os.CreateTemp("", "cpu-*.prof")
if err != nil {
fmt.Println("gagal membuat berkas profil:", err)
return
}
if err := pprof.StartCPUProfile(berkas); err != nil {
fmt.Println("gagal memulai profil CPU:", err)
return
}
hasil := kerjaRingan()
pprof.StopCPUProfile()
berkas.Close()
fmt.Println("hasil kerja:", hasil)
fmt.Println("profil CPU ditulis: true")
for _, nama := range []string{"goroutine", "threadcreate", "heap", "allocs", "block", "mutex"} {
profil := pprof.Lookup(nama)
ada := profil != nil
fmt.Printf("profil %-12s terdaftar: %v\n", nama, ada)
}
}Output yang Dihasilkan:
hasil kerja: 599999994
profil CPU ditulis: true
profil goroutine terdaftar: true
profil threadcreate terdaftar: true
profil heap terdaftar: true
profil allocs terdaftar: true
profil block terdaftar: true
profil mutex terdaftar: trueMari kita telaah baris demi baris program di atas:
- Baris 19 (
os.CreateTemp): membuat berkas sementara dengan polacpu-*.prof; nama pastinya dicetak runtime, dan program sengaja tidak mencetak path acak itu. - Baris 25–28 (
StartCPUProfile): mengaktifkan perekaman; setelah titik ini setiap 10 ms runtime mencatat stack yang sedang berjalan ke buffer profil. - Baris 29–31:
kerjaRinganadalah beban kerja yang diprofil; setelah selesai,StopCPUProfilemenghentikan perekaman dan memflush data, laluberkas.Close()menutup berkas. - Baris 33–34: hasil perhitungan dicetak untuk memastikan beban kerja benar-benar dieksekusi; profil ditulis ke berkas sementara.
- Baris 36–40:
pprof.Lookupmengambil handle profil non-CPU; loop membuktikan keenam nama pada tabel di atas terdaftar di runtime ini.
Membaca Profil dengan go tool pprof #
Setelah berkas profil ada di tangan, go tool pprof -top merangkumnya menjadi tabel per fungsi. Jalankan perintah berikut dengan nama berkas yang tercetak (ganti /tmp/cpu-XXXX.prof sesuai keluaran di mesin Anda):
go tool pprof -top /tmp/cpu-XXXX.profOutput yang Dihasilkan:
File: contoh
Build ID: 69b1edc00864c06c78a27e5e182a51649e564b4d
Type: cpu
Time: 2026-10-13 10:49:51 UTC
Duration: 123.82ms, Total samples = 120ms (96.91%)
Showing nodes accounting for 120ms, 100% of 120ms total
flat flat% sum% cum cum%
120ms 100% 100% 120ms 100% main.kerjaRingan (inline)
0 0% 100% 120ms 100% main.main
0 0% 100% 120ms 100% runtime.mainCara membaca tabelnya: kolom flat adalah waktu yang dihabiskan di tubuh fungsi itu sendiri, sedangkan cum (cumulative) menghitung waktu fungsi beserta seluruh pemanggilan yang dibuatnya. Nilai Duration: 123.82ms hanya mencakup jendela perekaman, bukan total umur program — proses go run berjalan lebih lama karena kompilasi. Baris File: contoh muncul karena metadata profil menyimpan nama binary sumber.
Dengan peta alat di tangan, kita akan menyelam lebih dalam ke profil CPU — menggabungkan dua fungsi dengan biaya berbeda lalu membandingkan kontribusinya — pada chapter berikutnya.
Referensi #
- Profiling Go Programs with pprof — pengantar resmi pprof oleh tim Go.
- Package runtime/pprof — dokumentasi resmi StartCPUProfile, Lookup, dan WriteTo.
- Diagnostics — posisi pprof dalam strategi diagnosis resmi Go.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh.go
Unduh berkaspackage main
import (
"fmt"
"os"
"runtime/pprof"
)
// kerjaRingan mensimulasikan beban CPU yang hasilnya selalu sama.
func kerjaRingan() int {
total := 0
for i := range 200_000_000 {
total += i % 7
}
return total
}
func main() {
berkas, err := os.CreateTemp("", "cpu-*.prof")
if err != nil {
fmt.Println("gagal membuat berkas profil:", err)
return
}
if err := pprof.StartCPUProfile(berkas); err != nil {
fmt.Println("gagal memulai profil CPU:", err)
return
}
hasil := kerjaRingan()
pprof.StopCPUProfile()
berkas.Close()
fmt.Println("hasil kerja:", hasil)
fmt.Println("profil CPU ditulis: true")
for _, nama := range []string{"goroutine", "threadcreate", "heap", "allocs", "block", "mutex"} {
profil := pprof.Lookup(nama)
ada := profil != nil
fmt.Printf("profil %-12s terdaftar: %v\n", nama, ada)
}
}