Why Memory Alignment Matters in Go: Performance, Pitfalls, and Tools
This article explains Go's memory alignment concepts, why alignment is needed, demonstrates real-world layout differences with examples, discusses performance and portability impacts, and introduces tools like fieldalignment and structlayout to detect and fix alignment issues in Go programs.
All examples in this article use a MacBook Pro M1, which has a 64‑bit CPU.
This is the third part of a Go high‑performance programming series, analyzing why memory alignment is required, the rules of Go memory alignment, practical examples, and two tools that help detect alignment problems during development.
What Is Memory Alignment?
Programmers can think of memory as a large array. An int16 occupies two bytes, and an int32 occupies four bytes.
type T1 struct {
a int8
b int64
c int16
}People unfamiliar with Go might assume this layout occupies 11 bytes, as shown in Figure 1.
In reality, printing the addresses of a T1 variable reveals a layout that occupies 24 bytes, as shown in Figure 2.
func main() {
t := T1{}
fmt.Println(fmt.Sprintf("%d %d %d %d", unsafe.Sizeof(t.a), unsafe.Sizeof(t.b), unsafe.Sizeof(t.c), unsafe.Sizeof(t)))
fmt.Println(fmt.Sprintf("%p %p %p", &t.a, &t.b, &t.c))
fmt.Println(unsafe.Alignof(t))
}
// output
// 1 8 2 24
// 0x14000114018 0x14000114020 0x14000114028
// 8CPU fetches data in units of its word size (e.g., 8 bytes on a 64‑bit CPU). This memory‑access granularity can cause performance degradation, loss of atomicity, and other unexpected issues.
Performance reduction due to extra CPU instructions.
Operations that were atomic become non‑atomic.
Other unforeseen problems.
Portability: Not all hardware can access arbitrary addresses for arbitrary data types.
Performance: Unaligned accesses require multiple memory reads, incurring extra clock cycles.
Go Memory Alignment
The Go specification defines alignment rules.
type size in bytes
byte, uint8, int8 1
uint16, int16 2
uint32, int32, float32 4
uint64, int64, float64, complex64 8
complex128 16For any variable x , unsafe.Alignof(x) is at least 1. For a struct variable, unsafe.Alignof(x) is the largest alignment of its fields, but at least 1. For an array variable, unsafe.Alignof(x) equals the alignment of its element type.
In most cases the Go compiler automatically aligns structs, but manual alignment is needed in some scenarios, such as 64‑bit atomic operations on 32‑bit platforms.
On x86, a 64‑bit atomic operation requires 8‑byte alignment; otherwise the program panics.
package main
import "sync/atomic"
type T3 struct {
b int64
c int32
d int64
}
func main() {
a := T3{}
atomic.AddInt64(&a.d, 1)
}This runs without error on amd64 but panics on i386 (Figure 3).
On 32‑bit platforms, T3 is 4‑byte aligned, while on 64‑bit it is 8‑byte aligned. The memory layouts are shown in Figures 4 and 5.
On 386, the 64‑bit functions use instructions unavailable before the Pentium MMX. On non‑Linux ARM, the 64‑bit functions use instructions unavailable before ARMv6k. On ARM, 386, and 32‑bit MIPS, the caller must ensure 64‑bit alignment for atomic operations.
To fix this, we manually pad T3 so it appears 8‑byte aligned:
type T3 struct {
b int64
c int32
_ int32 // padding
d int64
}Similar manual adjustments appear in Go's source and open‑source libraries such as mgc and groupcache.
Engineering Practice
fieldalignment
fieldalignmentis an official Go tool that detects possible memory‑alignment optimizations and can automatically fix them. For example, it suggests converting the original T1 struct to an aligned version.
➜ go_mem_alignment git:(main) ✗ fieldalignment -fix .
/Users/hxzhouh/workspace/github/blog-example/go/go_mem_alignment/main.go:8:8: struct of size 24 could be 16
// change
type T1 struct {
b int64
c int16
a int8
}The tool can also be enabled in golangci‑lint via the govet linter:
# .golangci.yml
linters:
disable-all: true
enable:
- govet
- fieldalignment
linters-settings:
govet:
check-shadowing: false
fast: falseNote that fieldalignment removes empty lines and comments when rearranging struct fields, so it is advisable to review changes via git diff before committing.
structlayout
structlayoutdisplays a struct’s layout and size, outputting SVG or JSON data. It is useful for visualizing and optimizing complex structs.
go install honnef.co/go/tools/cmd/structlayout@latest
go install honnef.co/go/tools/cmd/structlayout-pretty@latest
go install honnef.co/go/tools/cmd/structlayout-optimize@latest
go install github.com/ajstarks/svgo/structlayout-svg@latestAnalyzing T1 with structlayout:
structlayout -json ./main.go T1 | structlayout-svg >T1.svgThe output shows two padding gaps (7 bytes and 6 bytes). After reordering fields, the optimized T2 struct reduces padding:
type T2 struct {
a int8
c int16
b int64
}Conclusion
Memory alignment is a key technique in software design that improves performance and portability. This article used Go to illustrate the concept, showed actual memory layouts of structs, and explained why manual adjustments may be necessary.
Go’s alignment rules are applied automatically by the compiler, but developers sometimes need to reorder fields or add padding to avoid performance penalties and platform‑specific bugs.
Empty structs are also a useful tool for alignment optimization; see the related article “Golang High‑Performance Programming EP1: Empty Struct”.
Two practical tools help detect and fix alignment issues: fieldalignment: an official Go tool that automatically optimizes struct alignment. structlayout: visualizes struct layouts, aiding developers in understanding and optimizing memory usage.
Using these tools enables developers to maintain high performance and stability while reducing memory waste.
References
IBM DeveloperWorks: Data Alignment
Go Specification: Size and Alignment Guarantees
Citation Links
[1] go spec: https://go.dev/ref/spec#Size_and_alignment_guarantees
[2] atomic: https://godoc.org/sync/atomic#pkg-note-bug
[3] Int64: https://pkg.go.dev/sync/atomic#Int64
[4] Uint64: https://pkg.go.dev/sync/atomic#Uint64
[5] mgc: https://go.googlesource.com/go/.../mgc.go#L334
[6] groupcache: https://github.com/golang/groupcache/.../groupcache.go#L170
[7] fieldalignment: https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignment
[8] structlayout: https://github.com/dominikh/go-tools?tab=readme-ov-file
[9] Golang High‑Performance Programming EP1: Empty Struct: https://medium.com/gitconnected/decrypt-go-empty-struct-56640cd668e5
[10] IBM DeveloperWorks: Data Alignment: https://web.archive.org/web/20080607055623/http://www.ibm.com/developerworks/library/pa-dalign/
[11] Go Specification: Size and Alignment Guarantees: https://golang.google.cn/ref/spec#Size_and_alignment_guarantees
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
