Episode ini mengukur dan mengoptimalkan performa Go: profiling dengan pprof, benchmarking dengan go test -bench, analisis allocation dengan escape analysis, serta teknik tuning seperti buffer reuse dan zero allocation patterns.

Episode 13 menjawab satu pertanyaan: apakah aplikasi Go kalian cepat? Bukan dengan tebakan, melainkan dengan pengukuran. Go menyediakan tooling profiling dan benchmarking kelas dunia langsung dari toolchain resminya.
Kalian akan belajar empat hal: profiling CPU dan memori dengan pprof, benchmarking dengan go test -bench, memahami alokasi memori dan escape analysis, serta teknik performance tuning seperti buffer reuse dan zero allocation patterns. Fokusnya selalu: ukur dulu, optimalkan yang berdampak.
net/http/pprof menyediakan endpoint profiling untuk aplikasi yang berjalan. Import blank cukup untuk mengaktifkannya di server HTTP.
package main
import (
"net/http"
_ "net/http/pprof"
)
func main() {
http.ListenAndServe(":8080", nil)
}Saat aplikasi berjalan, akses /debug/pprof/ untuk daftar profil, /debug/pprof/profile untuk CPU profile selama 30 detik, dan /debug/pprof/heap untuk heap snapshot. Jangan aktifkan pprof di endpoint publik tanpa autentikasi.
Kumpulkan profil lalu analisis dengan go tool pprof:
curl -o cpu.prof http://localhost:8080/debug/pprof/profile?seconds=30
go tool pprof cpu.profDi dalam shell interaktif, perintah top menampilkan fungsi yang paling banyak memakai CPU, list namaFungsi menunjukkan per baris, dan web memvisualisasikan grafik call. Setelah melihat profil, optimalkan hanya fungsi yang benar-benar dominan.
Benchmark (dari episode 6) menjadi dasar perbandingan. Saat mengoptimalkan, ukur kondisi sebelum dan sesudah, lalu bandingkan:
func BenchmarkGabungBuffer(b *testing.B) {
for i := 0; i < b.N; i++ {
var sb strings.Builder
for j := 0; j < 100; j++ {
sb.WriteString("data")
}
_ = sb.String()
}
}Jalankan dengan go test -bench=. -benchmem -count=5 ./... untuk hasil yang lebih stabil. -count mengulang benchmark agar noise jaringan dan OS bisa dirata-ratakan.
Compiler Go bisa menghilangkan kode yang hasilnya tidak terpakai. Setiap hasil benchmark sebaiknya disimpan ke package-level variable agar tidak dioptimalkan.
var hasilBenchmark string
func BenchmarkKode(b *testing.B) {
var s string
for i := 0; i < b.N; i++ {
s = gabungkan()
}
hasilBenchmark = s
}Escape analysis menentukan apakah sebuah variabel hidup di stack (murah) atau heap (mahal, butuh GC). Variabel yang "melarikan diri" ke luar fungsi — dikembalikan, disimpan di pointer, atau ditangkap closure — dialokasikan ke heap.
func buatPointer() *int {
n := 42
return &n
}Jalankan go build -gcflags="-m" main.go untuk melihat keputusan compiler. Jika output menyebut moved to heap, objek dialokasikan di heap. Mengurangi alokasi heap menurunkan beban garbage collector secara langsung. Alternatifnya, go build -gcflags="-m" main.go tetap menghasilkan binary meskipun menampilkan analisis alokasi.
Setiap alokasi heap menambah kerja GC: semakin banyak alokasi, semakin sering GC berjalan. Itulah mengapa pola zero allocation di kode hot path bisa meningkatkan throughput secara signifikan.
Untuk pembacaan data yang berulang, alokasikan buffer sekali lalu pakai berulang kali. sync.Pool bahkan memungkinkan buffer dibagi antar goroutine:
var bufPool = sync.Pool{
New: func() any {
return make([]byte, 1024)
},
}
func proses(data []byte) {
buf := bufPool.Get().([]byte)
defer bufPool.Put(buf)
copy(buf, data)
// proses data
}sync.Pool mengembalikan objek yang pernah dibuat dan membuangnya saat GC berjalan. Cocok untuk buffer sementara, bukan untuk state yang harus bertahan.
append yang berulang memicu reallocation ketika kapasitas penuh. Beri tahu kapasitas awal saat membuat slice agar alokasi cukup sekali:
hasil := make([]int, 0, 1000)
for _, v := range sumber {
hasil = append(hasil, v)
}Dengan make([]int, 0, 1000), kapasitas dicadangkan di awal. Untuk ribuan elemen, ini menghindari puluhan reallocation yang tidak perlu.
Beberapa praktik lain: reuse struct, hindari pointer ke data kecil, dan gunakan range atas value untuk data immutable. Jangan pernah mengoptimalkan sebelum profil menunjukkan masalah — optimasi prematur hanya menambah kompleksitas.
Episode 13 membekali kalian siklus pengukuran dan optimasi: profiling dengan pprof dan go tool pprof, benchmark dengan go test -bench dan -benchmem, pemahaman escape analysis dan alokasi heap, serta teknik buffer reuse, sync.Pool, dan kapasitas slice yang tepat.
Inti yang harus dibawa pulang:
-benchmem dan -count untuk hasil stabil.sync.Pool me-reuse buffer lintas goroutine.append tidak sering reallocate.Di episode 14 selanjutnya kita akan membahas observability, logging, dan tracing — logging terstruktur dengan zap, logrus, atau zerolog, tracing terdistribusi dengan OpenTelemetry, dan metrik terstruktur dengan Prometheus client. Aplikasi kalian akan bisa diintrospeksi saat berjalan di production.