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 profilYang direkamPertanyaan yang dijawab
cpuSampel waktu CPU selama perekamanFungsi mana memakan waktu prosesor?
heapObjek yang masih hidup (inuse)Memori dipakai siapa saat ini?
allocsTotal alokasi kumulatif sejak mulaiAlokasi datang dari stack mana?
goroutineStack semua goroutineGoroutine macet/blocking di mana?
threadcreateThread OS yang dibuat runtimeBerapa thread sistem dipakai?
blockWaktu menunggu channel/mutexLatensi sinkronisasi seberapa besar?
mutexPemegang dan penantang mutexKontensi pada mutex mana?

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:

go
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:

text
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: true

Mari kita telaah baris demi baris program di atas:

  1. Baris 19 (os.CreateTemp): membuat berkas sementara dengan pola cpu-*.prof; nama pastinya dicetak runtime, dan program sengaja tidak mencetak path acak itu.
  2. Baris 25–28 (StartCPUProfile): mengaktifkan perekaman; setelah titik ini setiap 10 ms runtime mencatat stack yang sedang berjalan ke buffer profil.
  3. Baris 29–31: kerjaRingan adalah beban kerja yang diprofil; setelah selesai, StopCPUProfile menghentikan perekaman dan memflush data, lalu berkas.Close() menutup berkas.
  4. Baris 33–34: hasil perhitungan dicetak untuk memastikan beban kerja benar-benar dieksekusi; profil ditulis ke berkas sementara.
  5. Baris 36–40: pprof.Lookup mengambil 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):

bash
go tool pprof -top /tmp/cpu-XXXX.prof

Output yang Dihasilkan:

text
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.main

Cara 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 #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

contoh.go

Unduh berkas
go
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)
	}
}