“Fast is fine, but profiling tells you why you’re slow.”
Performance issues are often hard to debug — your app feels sluggish, CPU spikes randomly, or memory usage keeps growing. Fortunately, Go provides powerful built-in tools to profile applications and uncover these bottlenecks.
In this article, I’ll walk through profiling techniques in Go
, focusing on CPU
, memory
, goroutine
, and concurrency
analysis using tools like pprof
and trace
.
🔍 What Is Profiling?
Profiling is the act of measuring the performance characteristics of your application:
Where is the CPU time being spent?
How much memory is being allocated?
Are goroutines being leaked?
Is concurrency causing contention?
Go’s standard library includes everything you need to answer these questions.
⚙️ net/http/pprof: Built-in Profiler
The simplest way to expose profiling data is to import:
|
|
Add this to your HTTP server:
|
|
Then, access these endpoints:
/debug/pprof/profile
— CPU profile/debug/pprof/heap
— memory profile/debug/pprof/goroutine
— goroutine dump
🧠 CPU Profiling
Generate a CPU profile:
|
|
Analyze it with:
|
|
**web
opens a flame graph (requires Graphviz
) **
🧠 Memory Profiling
Generate a heap profile:
|
|
Look for high allocation counts and large retained objects.
Use pprof -alloc_objects, -inuse_space to slice the data differently.
🧵 Goroutines and Blocking
Dump goroutines:
|
|
Find out if:
Goroutines are leaking
Something is blocking channels or mutexes
⚡ Execution Tracing
Go also supports full execution traces:
|
|
Wrap your code:
|
|
Then run:
|
|
Use this to spot scheduling delays, GC pauses, network latency, etc.
🧪 Benchmarking + Profiling
You can combine unit tests and profiling:
|
|
Run with profiling:
|
|
🛠️ Real-World Tips
Profile in production with real workloads when possible
Use flame graphs to spot hot loops and recursive calls
Compare snapshots before and after changes
Combine
pprof
with metrics (Prometheus
,Grafana
)
🧭 Summary
Profiling Go applications is straightforward but incredibly powerful:
Use pprof for CPU, memory, and goroutines
Use trace for low-level runtime behavior
Benchmark with go test to validate changes
Profile before you optimize — measure twice, cut once.
🚀 Follow me on norbix.dev for more insights on Go, system design, and engineering wisdom.