Go Profiling — Performance Profiling with pprof and Trace for CPU, Memory, and Goroutines
In this tutorial, you will learn about Go Profiling. We cover key concepts, practical examples, and best practices to help you master this topic.
Go pprof profiles CPU, memory, Goroutine, and block contention with runtime/pprof and net/http/pprof for production profiling.
What You'll Learn
- CPU profiling
- Memory profiling
- HTTP pprof endpoints
- Trace execution
Why It Matters
Profiling identifies performance bottlenecks. Docker profiles build performance. Kubernetes profiles API server latency. DodaZIP profiles compression throughput.
Real-World Use
Production performance debugging, memory leak detection, goroutine leak detection, latency optimization.
flowchart LR
A["Profiling"] --> B["CPU Profile"]
A --> C["Memory Profile"]
A --> D["HTTP pprof"]
A --> E["Trace"]
A:::current --> B
style A fill:#2563eb,stroke:#2563eb,color:#fff
style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b
CPU Profiling
func main() {
f, _ := os.Create("cpu.prof")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// Code to profile
expensiveOperation()
}
Memory Profiling
func main() {
for i := 0; i < 100; i++ {
allocateMemory()
}
f, _ := os.Create("mem.prof")
pprof.WriteHeapProfile(f)
f.Close()
}
HTTP pprof Endpoints
import _ "net/http/pprof"
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Application code
select {}
}
Available endpoints:
/debug/pprof/ — Overview
/debug/pprof/profile — CPU profile (30s)
/debug/pprof/heap — Memory profile
/debug/pprof/goroutine — Goroutine stack traces
/debug/pprof/block — Block contention
/debug/pprof/mutex — Mutex contention
Profiling Analysis
# CPU
go tool pprof cpu.prof
(pprof) top10
(pprof) web
(pprof) list functionName
# Interactive web UI
go tool pprof -http=:8080 cpu.prof
# From HTTP endpoint
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
Tracing
func main() {
f, _ := os.Create("trace.out")
trace.Start(f)
defer trace.Stop()
// Code to trace
myFunction()
}
go tool trace trace.out
Goroutine Profile
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() { defer wg.Done(); time.Sleep(time.Second) }()
}
wg.Wait()
// Take goroutine profile
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
}
Common Mistakes
1. Profiling Without Workload
Profiling idle code gives meaningless results. Profile under realistic load.
2. Short Profiles
CPU profile default is 30 seconds. Shorter profiles may miss intermittent issues.
3. Profiling in Development Only
Production profiles reveal real bottlenecks. Use HTTP pprof in production with authentication.
4. Ignoring Goroutine Leaks
Monitor goroutine count in production. A growing count indicates leaks.
5. Not Using -http Flag
The web UI (go tool pprof -http=:8080) is more useful than CLI for exploring profiles.
Practice Questions
1. What's the difference between CPU and memory profiling? CPU profile shows where time is spent. Memory (heap) profile shows allocation hotspots.
2. How do you profile a production Go service? Import net/http/pprof, expose /debug/pprof on a separate port with authentication.
3. What does the trace tool show? Goroutine creation, GC events, network blocking, syscalls, and scheduler activity over time.
4. How do you detect a goroutine leak? Compare goroutine count over time. pprof.Lookup("goroutine").Count() shows active goroutines.
Challenge: Profile a function and identify the top 3 CPU hot spots.
Solution
# Add import _ "net/http/pprof" to your main
# Run the program
# Collect 30s CPU profile
go tool pprof http://localhost:6060/debug/pprof/profile
# In pprof shell:
top10 # Show top 10 hot spots
web # Show flame graph
list hotFunc # Show line-by-line breakdown
FAQ
{{< faq question="What is the difference between pprof and trace?" >}} pprof shows sampling profiles (CPU, memory, goroutines). Trace shows event timeline (goroutine, GC, syscall). Use pprof for bottlenecks, trace for concurrency issues. {{< /faq >}}
{{< faq question="How do I compare two profiles?" >}}
Use go tool pprof -base base.prof current.prof. Shows the difference between profiles.
{{< /faq >}}
{{< faq question="Is profiling safe in production?" >}} CPU profiling adds ~5% overhead. Memory profiling is lighter. Enable pprof on an internal port behind authentication. {{< /faq >}}
{{< faq question="What is a flame graph?" >}}
A visualization of stack traces. Width represents time spent. Use go tool pprof -http to view interactive flame graphs.
{{< /faq >}}
{{< faq question="How do I profile a running binary?" >}} Send SIGQUIT (Ctrl+\ or kill -QUIT) to dump all goroutine stacks. Or use HTTP pprof endpoints on the running server. {{< /faq >}}
Try It Yourself
package main
import (
"os"
"runtime/pprof"
)
func main() {
f, _ := os.Create("cpu.prof")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
sum := 0
for i := 0; i < 1000000; i++ {
sum += i * i
}
_ = sum
}
Analyze with:
go run main.go
go tool pprof cpu.prof
(pprof) top
Expected output — CPU profile showing the tight loop.
What's Next
Now that you understand profiling, explore Go modules for dependency management.
| Topic | Description | Link |
|---|---|---|
| Go Modules | Package management | {{< ref "37-modules" >}} |
| Go Benchmarking | Performance Testing | {{< ref "35-benchmarking" >}} |
| Go CLI Apps | Building CLI tools | {{< ref "39-cli-apps" >}} |