Hi, my name is Walid, a backend developer who’s currently learning Go and sharing my journey by writing about it along the way.
Resource :


When writing high-performance Go programs especially those that involve multithreading, networking, or low-level system access understanding how Go places struct fields in memory can dramatically improve both speed and memory usage.

This article breaks down concepts like alignment, padding, and cache-line usage, and shows how to spot and fix hidden inefficiencies. The article is written with clarity in mind while providing insights useful for both beginners and more advanced developers.


What Is Alignment?

Each data type in Go prefers to be stored at a memory address that's a multiple of its size:

  • uint8 → aligned to 1 byte
  • uint16 → aligned to 2 bytes
  • uint32 → aligned to 4 bytes
  • uint64 → aligned to 8 bytes
  • float64, int64 → aligned to 8 bytes

Why? Because CPUs are much faster at reading aligned memory. Misalignment can cause performance penalties and even errors on some architectures.

Padding Table by Data Type

Type Size (bytes) Alignment Padding After (If Needed)
uint8 1 1 Up to 7 bytes
uint16 2 2 Up to 6 bytes
uint32 4 4 Up to 4 bytes
uint64 8 8 0

What Is Padding and Why Does It Exist?

Padding is extra memory added by the Go compiler between struct fields to maintain proper alignment.

// Misaligned layout
type Bad struct {
    A uint32 // offset 0
    B uint64 // offset 4 (misaligned)
}

To fix this, the compiler adds 4 bytes of padding after A:

// Aligned layout
type Good struct {
    B uint64 // offset 0
    A uint32 // offset 8
}

Memory Layout Schema

Bad:   | A (4) | pad (4) | B (8) | => 16 bytes
Good:  | B (8) | A (4) + pad (4)   | => 16 bytes

Field Ordering Matters: Save Megabytes

Ordering fields by descending size can significantly reduce padding and save memory, especially when creating many struct instances.

Example

type PoorLayout struct {
    A uint8   // 1 byte
    B uint64  // 8 bytes (misaligned)
}

This layout forces 7 bytes of padding → ~7MB wasted for 1 million instances.

Better layout:

type OptimizedLayout struct {
    B uint64
    A uint8
    _ [7]byte // explicit padding if needed
}

Optimizing layout this way can save memory and reduce CPU cache pressure.


Struct Size and Alignment on 32-bit vs 64-bit Systems

System architecture affects alignment:

  • 32-bit systems: Align to 4 bytes
  • 64-bit systems: Align to 8 bytes
type Info struct {
    Flags    uint32
    RegUse   uint64
    RegSet   uint64
    RegIndex uint64
}
  • 32-bit: 4 + 8 + 8 + 8 = 28 bytes
  • 64-bit: 4 + 4 (padding) + 8 + 8 + 8 = 32 bytes

Go may also add trailing padding to ensure the struct size is a multiple of the largest field's alignment, especially for array usage.


The Empty struct{} Trick (and Its Gotchas)

While struct{} consumes no storage, you can still take its address. This forces Go (since 1.5) to treat it as if it were 1 byte when at the end of a struct.

type WithEmpty struct {
    A uint32
    Z struct{}
}

This layout adds 3 bytes of padding to align the full struct to a 4-byte boundary on 32-bit systems.

Schema

A (4) + Z (1) + pad (3) = 8 bytes

Move zero-sized fields like struct{} to the top of the struct to avoid unnecessary end padding.


Cache Line Considerations

Modern CPUs load memory in 64-byte cache lines. If two goroutines update fields in the same cache line, they interfere with each other this is called false sharing.

type Shared struct {
    A int64
    B int64
}

If A and B are written concurrently, they may sit in the same cache line, slowing things down due to cache invalidations.

Optimizing with Padding

type Padded struct {
    A int64
    _ [56]byte // to isolate B on another cache line
    B int64
}

This ensures each field is on a separate cache line, improving performance in concurrent contexts.


Go’s Size Classes and Allocation Efficiency

Go groups small allocations into size classes to optimize memory usage. Example:

  • A 40-byte struct → rounded up to a 48-byte size class
  • Reduce it to 32 bytes → fits in a smaller 32-byte class

Benefit

Reducing struct size from 296 bytes to 288 bytes moves it from a 320-byte class to 288 → saving 32 bytes per allocation.

In large systems, this adds up to megabytes in savings.


Summary: Practical Struct Design Guidelines

  • Order fields by size: Largest to smallest to reduce padding
  • Separate concurrent fields: Avoid false sharing using cache line awareness
  • Track alignment requirements: Especially for cross-architecture compatibility
  • Move zero-sized types to the top: Avoid trailing padding
  • Use benchmarks: Profile before and after changes
  • Keep structs compact: Memory adds up fast at scale

By understanding Go's struct layout rules and CPU memory behavior, you can write code that's both fast and memory efficient especially in high-scale and concurrent environments.