Are Pointers in Go Faster Than Values?
I was recently working on a lesson about pointer performance for Boot.dev’s Golang course when I found myself repeating some advice I’ve given many times before.
Begin quote:
Occasionally, new Go developers hear “pointers don’t pass copies” and take that to a logical extreme, concluding: “Pointers are always faster because copying is slow. I’ll always use pointers!”
No. Bad. Stop.
Here are my rules of thumb:
- First, worry about writing clear, correct, maintainable code.
- If you have a performance problem, fix it.
Before even thinking about using pointers to optimize your code, use pointers when you need a shared reference to a value; otherwise, just use values.
If you do have a performance problem, consider:
- Stack vs. Heap
- Copying
Interestingly, local non-pointer variables are generally faster to pass around than pointers because they’re stored on the stack, which is faster to access than the heap. Even though copying is involved, the stack is so fast that it’s no big deal.
Once the value becomes large enough that copying is the greater problem, it can be worth using a pointer to avoid copying. That value will probably go to the heap, so the gain from avoiding copying needs to be greater than the loss from moving to the heap.
One of the reasons Go programs tend to use less memory than Java and C# programs is that Go tends to allocate more on the stack.
End quote
Is this advice accurate? How can we know?
While my research into the Go docs, various community articles, and even consulting the all-knowing (lul) Chat GPT confirmed the validity of my advice, I wanted to see for myself.
So, I wrote this benchmark that you can try for yourself if you’re curious!
package main
import (
"fmt"
"testing"
)
type data struct {
a, b, c, d, e, f, g, h, i, j int64
}
var globalPtr *data
var globalValue data
func newDataPtr(i int) *data {
data := &data{int64(i), int64(i + 1), int64(i + 2), int64(i + 3), int64(i + 4), int64(i + 5), int64(i + 6), int64(i + 7), int64(i + 8), int64(i + 9)}
return data
}
func newData(i int) data {
data := data{int64(i), int64(i + 1), int64(i + 2), int64(i + 3), int64(i + 4), int64(i + 5), int64(i + 6), int64(i + 7), int64(i + 8), int64(i + 9)}
return data
}
func BenchmarkProcessValue(b *testing.B) {
for i := 0; i < b.N; i++ {
globalValue = newData(i)
}
// use it to avoid compiler optimization
fmt.Println(globalValue.a)
}
func BenchmarkProcessPointer(b *testing.B) {
for i := 0; i < b.N; i++ {
globalPtr = newDataPtr(i)
}
// use it to avoid compiler optimization
fmt.Println(globalPtr.a)
}
Slap that in a bench_test.go file and run go test -bench=. -benchmem to see the results. This is what I got:
wagslane@MacBook-Pro-2 test % go test -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: github.com/bootdotdev/go-api-gate/test
BenchmarkProcessValue-12 273343356 4.236 ns/op 0 B/op 0 allocs/op
BenchmarkProcessPointer-12 61566219 17.72 ns/op 80 B/op 1 allocs/op
PASS
ok github.com/bootdotdev/go-api-gate/test 2.912s
As you can see, passing by value rather than reference (pointer) is indeed faster in this case, even though the value is being copied.
That said, I admit it took me about 20 minutes of trial and error to get this benchmark into the state that I wanted to make sure it was testing what I wanted to test. There were initial drafts that I thought were copying to the heap, but they weren’t. That in and of itself is a good lesson: the Go compiler is pretty smart and will optimize things for you. Don’t go crazy trying to use pointers or non-pointers to optimize your code until you have something tangible to benchmark and optimize!
Related Articles
Can Go Be Used in Web Development?
Oct 06, 2023 by Natalie Schooner - Computer science educator and technical writer
Recently I saw an interesting post on Reddit: “I would like to be more full-stack,” user Fenugurod said. “I was studying Tailwindcsss and I’m pretty sure I can create really nice UIs with it. But what do you guys think about web development with Go? Most of my friends simply say to embrace the JS ecosystem with Nuxt or Next and use Go simply as an API.”
The One Thing I'd Change About Go
Aug 12, 2023 by Lane Wagner - Boot.dev co-founder and backend engineer
Go is built for grug brained programmers like me. grug brain developer not so smart, but grug brain developer program many long year and learn some things although mostly still confused
Format on Save in Go with VS Code [2026]
May 26, 2023 by Lane Wagner - Boot.dev co-founder and backend engineer
Go has hard opinions about how you should style and format your code. Setting up your VS Code environment to enforce the standard linting and formatting rules can save you a ton of time.
How to Become a Golang Engineer (on the Back-End)
Feb 23, 2023 by Natalie Schooner - Computer science educator and technical writer
“Guys, I’ve got an idea. What if we could design a language that’s easy to read like Python, but fast? That has a slim feature-set like C, but is good for web development? That’s compiled like Java, but doesn’t need a VM?”