Profiling Go Code: Finding Bottlenecks
Performance optimization is one of the key tasks for a developer, especially when dealing with high-load systems in Go. However, before optimizing anything, you need to know exactly which code sections are bottlenecks. This is where profiling comes in — collecting and analyzing program execution metrics.
In this article, we will cover how to profile Go applications using built-in tools: pprof and trace. You will learn how to find problem areas related to CPU, memory, blocking, and goroutines, as well as how to interpret the obtained data.
What is profiling and why is it needed?
Profiling is the process of collecting data about a program's operation during its execution. This data helps answer questions:
- Which functions consume the most CPU time?
- Where does a large amount of memory allocation occur?
- Which goroutines are blocked and on what?
- How long does a system call or I/O operation take?
Go provides powerful profiling tools right out of the box. The main ones are net/http/pprof for web applications and runtime/pprof for CLI utilities.
The pprof tool: CPU and Memory profiles
pprof is the main tool for profiling Go. It can collect CPU, memory, blocking, goroutine, and mutex contention profiles.
Adding pprof to a web application
The easiest way to enable profiling is to import the net/http/pprof package:
package main
import ( "net/http" _ "net/http/pprof" // registers pprof handlers)
func main() { // Your application http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil)}After this, various profiles will be available at /debug/pprof/. For example:
/debug/pprof/profile— CPU profile (default 30 seconds)/debug/pprof/heap— memory profile (heap)/debug/pprof/goroutine— dump of all goroutines/debug/pprof/block— blocking profile
Collecting a CPU profile
To collect a CPU profile, use the command:
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30After collection completes, the interactive pprof console will open. The most useful commands:
top— show functions consuming the most CPUlist <function>— show line-by-line time breakdownweb— open call graph in browser (requires Graphviz)peek <function>— show callers and callees
Example top output:
Showing nodes accounting for 3.50s, 70% of 5.00s total flat flat% sum% cum cum% 1.20s 24.00% 24.00% 1.20s 24.00% encoding/json.(*Decoder).Decode 0.80s 16.00% 40.00% 0.80s 16.00% runtime.mallocgc 0.60s 12.00% 52.00% 1.80s 36.00% main.processData 0.50s 10.00% 62.00% 0.50s 10.00% runtime.memmove 0.40s 8.00% 70.00% 0.40s 8.00% strings.ToLowerCollecting a memory profile
To analyze memory usage:
go tool pprof http://localhost:8080/debug/pprof/heapImportant commands in pprof for memory:
top— functions allocating the most memorysample_index=alloc_space— switch to total allocated memorysample_index=inuse_space— switch to currently used memorytr