All Posts

Unlocking Go Performance: The Art of Struct Field Ordering

January 14, 2025

Go

Performance

Optimization

When shipping performance-critical services in Go, every optimization matters. One area for improvement is the order of fields in a struct. I realized this during my internship when my senior suggested reordering fields in my PR. At first, I thought doing this manually would be a overwhelming, but thankfully, there's an easier way. Let's dive into why field ordering in structs matters.

Putting It to the Test

Let's define two identical structs with the same fields and create a simple program to compare their memory usage.

cmd/main.go
type (
	Bad struct {
		IsActive  bool
		TotalTime int64
		AvgScore  float32
	}
	Good struct {
		TotalTime int64
		AvgScore  float32
		IsActive  bool
	}
)
 
func main() {
	fmt.Println(unsafe.Sizeof(Bad{}))
	fmt.Println(unsafe.Sizeof(Good{}))
}

The code output shows that the Bad struct occupies 24 bytes, while the Good struct occupies only 16 bytes. Despite having nearly identical structs, the difference in memory usage comes down to how data is arranged in memory, specific in terms of data structure alignment.

Data Structure Alignment & Padding

In a 64-bit system, the CPU accesses memory in word-size units (8 bytes). It reads data more efficiently when the data is stored at addresses that are multiples of its word size. Misaligned data requires extra cycles to fetch, making it slower and less efficient.

Word 1Word 2Word 3Word 1Word 2Bad Struct (24 byte)Good Struct (16 byte)IsActiveTotalTimePaddingAvgScore
Padding ensures proper data alignment, which can result in the data not being perfectly aligned to word boundaries in some cases.

Padding is essential for achieving data alignment. Padding adds extra bytes between data fields to ensure proper alignment in memory. While this increases memory usage, it improves access speed by ensuring that each variable starts at a memory address that is a multiple of its size.

Let's Make It Effortless

During a recent PR review, I noticed that managing structs with 20+ fields across multiple layers (such as controllers, services, and repositories) can quickly become overwhelming. Each layer often defines its own domain-specific struct, and manually organizing them to optimize memory usage is a headache. Luckily, Go offers a powerful tool to help automate this process, called fieldalignment.

This tool automatically reorders the fields in your structs to optimize memory layout and reduce padding, ensuring efficient use of memory.

Here's how you can set it up:

  1. Install the tool globally using the following command.

    go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest
  2. Run the tool to automatically reorder all you're structs fields in your project.

    fieldalignment -fix ./...

This command will recursively scan all directories and adjust the struct field order for optimal memory usage.

Wrapping It Up

Optimizing struct field ordering may seem like a small detail, but in large-scale Go applications, it can have a big impact. Poor field ordering wastes memory, slows down access, and adds unnecessary complexity as your codebase grows. With tools like fieldalignment, developers can automate this process, improving efficiency and allowing them to focus more on building robust applications.

Built with Next.js, MDX, Tailwind and Vercel