Go Performance: How to Profile and Speed Up Code

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Your Go code running slow? Time to look under the hood

\n\n

You wrote a service in Go. It works. But when the load reaches 10,000 RPS, the server starts to "choke." Sound familiar? Go is famous for its "out of the box" performance, but even this elegant language has its pitfalls. Goroutine leaks, inefficient memory management, unnecessary allocations — all of this kills speed.

\n\n

In this article, we'll break down how Go profiling helps find bottlenecks and show specific techniques for optimizing Go applications. You'll learn how to turn your application into a rocket using the built-in pprof and go test -bench tools. Let's go!

\n\n

Profiler toolkit: pprof and trace

\n\n

Go provides two powerful tools for performance analysis: pprof (CPU, memory, and lock profiling) and go tool trace (concurrency and GC event analysis).

\n\n

Embedding pprof into an HTTP server

\n\n

The easiest way to start profiling is to add net/http/pprof to your server. This takes exactly one import line:

\n\n
import (\n    _ "net/http/pprof"\n    "net/http"\n)\n\nfunc main() {\n    http.ListenAndServe(":8080", nil)\n}
\n\n

Now profiles are available at /debug/pprof/. To capture a CPU profile for 30 seconds, run:

\n\n
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30
\n\n

After data collection, an interactive console starts. The top15 command will show the hottest functions:

\n\n
(pprof) top15\nShowing nodes accounting for 3.50s, 87.50% of 4.00s total\n      flat  flat%   sum%        cum   cum%\n     1.20s 30.00% 30.00%      1.50s 37.50%  main.heavyComputation
\n\n

See main.heavyComputation? That's our candidate for optimization. For visualization, use web (if Graphviz is installed) — you'll get a call graph.

\n\n

Memory profiling: finding leaks

\n\n

Memory leaks in Go are most often goroutine leaks or forgotten references to large objects. Let's capture a heap profile:

\n\n
go tool pprof http://localhost:8080/debug/pprof/heap
\n\n

The alloc_objects command will show which functions create the most objects:

\n\n
(pprof) alloc_objects\n    2.50GB  100%  100%      2.50GB  100%  main.createLargeSlice
\n\n

That's how we found a function allocating 2.5 GB of memory! Perhaps it's creating a huge slice that could be reused with sync.Pool.

\n\n

Writing benchmarks: your first step to optimization

\n\n

Before optimizing, you need to measure. Go benchmarks are the standard tool for measuring the performance of individual functions.

\n\n

Example: comparing string concatenation

\n\n

Let's say we're concatenating strings in a loop. The bad way:

\n\n
func ConcatSlow(parts []string) string {\n    result := ""\n    for _, p := range parts {\n        result += p\n    }\n    return result\n}
\n\n

The good way — use strings.Builder:

\n\n
func ConcatFast(parts []string) string {\n    var b strings.Builder\n    for _, p := range parts {\n        b.WriteString(p)\n    }\n    return b.String()\n}
\n\n

Let's write a benchmark (file concat_test.go):

\n\n
func BenchmarkConcatSlow(b *testing.B) {\n    parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n    for i := 0; i < b.N; i++ {\n        ConcatSlow(parts)\n    }\n}\n\nfunc BenchmarkConcatFast(b *testing.B) {\n    parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n    for i := 0; i < b.N; i++ {\n        ConcatFast(parts)\n    }\n}
\n\n

Run it:

\n\n
go test -bench=. -benchmem
\n\n

Result:

\n\n
BenchmarkConc                

Blogs

Book Recommendations