Go has excellent built-in profiling tools. Here is how to use them.
Activating pprof¶
import _ “net/http/pprof” go func() { log.Println(http.ListenAndServe(“:6060”, nil)) }()
CPU Profiling¶
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
Memory¶
go tool pprof http://localhost:6060/debug/pprof/heap
Goroutines¶
curl http://localhost:6060/debug/pprof/goroutine?debug=1
Flame Graphs¶
go tool pprof -http=:8080 cpu.prof
Benchmarks¶
func BenchmarkMyFunc(b *testing.B) { for i := 0; i < b.N; i++ { MyFunc() } } go test -bench=. -benchmem
Optimization¶
- sync.Pool for repeated allocations
- Preallocate slices
- strings.Builder
- Buffered channels
Practical Profiling Workflow¶
When optimizing a Go application, proceed systematically: first identify the bottleneck using a CPU profile, then analyze allocations using a heap profile. A flame graph visualizes the call stack and shows where the application spends the most time — wide blocks indicate hot paths.
The Go runtime also provides a trace tool (go tool trace) that displays a detailed timeline of goroutines, GC pauses, and scheduler activities. For production applications, enable continuous profiling using Pyroscope or Parca — you collect profiles with minimal overhead and can analyze performance retroactively. Key Go optimizations: use sync.Pool for repeated allocations, preallocate slices with make([]T, 0, expectedSize), and prefer strings.Builder over string concatenation.
Summary¶
pprof is built-in and powerful. CPU + heap covers 90% of problems.