Go Benchmark
Struct Copy vs Pointer
Does passing structs by pointer actually make your code faster?
"Always pass structs by pointer, copying is slow" is one of the most repeated pieces of Go performance advice — and it is often wrong. This benchmark passes and returns a small 32-byte struct and a large 4 KiB struct both by value and by pointer, plus the constructor case where returning a pointer forces the value to escape to the heap. Copying small structs is essentially free, while returning large structs by pointer trades a stack copy for a heap allocation and future garbage-collector work. All functions are marked //go:noinline so the calls and copies are actually measured instead of optimized away.
1 CPU
32 CPUs
Mutates the same 32-byte struct through a pointer. Saves the tiny copy, but every access goes through an indirection, and sharing mutable state through pointers is exactly what escape analysis and the garbage collector have to be pessimistic about.
Large Struct By Pointer #
Mutates the 4 KiB struct in place through a pointer. No copies at all, independent of struct size — for genuinely large structs on hot paths this is the clear winner.
Small Struct By Value #
Passes and returns a 32-byte struct by value. The struct fits in a few registers or one cache line, so the "copy" is essentially free — this is why the Go standard library passes small values like time.Time by value.
Return Large By Value #
A constructor that returns a 4 KiB struct by value into a caller-owned variable. The result never escapes, so the whole operation happens on the stack with zero heap allocations — the copy is cheaper than a garbage-collected allocation.
Large Struct By Value #
Passes and returns a 4 KiB struct by value, copying it into the callee's frame and back on every call. Here the copy cost is real and grows linearly with struct size — this is the case the "use pointers" advice is actually about.
A constructor that returns *LargeStruct. Escape analysis has to move the local onto the heap, so every call allocates 4 KiB that the garbage collector must track and reclaim. Watch the allocation columns: this is the hidden cost of the "just return a pointer" reflex.
Contributors