← Back to the home page

Struct field ordering in Go

Go has structs. Here's one:

type structName struct {
	b bool
	i int64
}

defined by the spec as a sequence of named elements, called fields, each of which has a name and a type, the concept is straight-forward.

In this page are some things about them.

Index

1. In memory, the order of the fields is preserved

When a struct is allocated, its fields are laid out in memory in the same order as they are defined in code. As of 2015, this was not guaranteed in the spec[1] but by the default compiler. I believe this is still the case; I don't know what other compilers (tinygo[2], gollvm[3]) do.

Note that 'in the same relative order' does not mean 'in the same relative order AND contiguously'; see next point.

2. Go may insert padding between fields in a struct

From the spec:

Computer architectures may require memory addresses to be aligned; that is, for addresses of a variable to be a multiple of a factor, the variable's type's alignment. [4]

Bytes ('padding') may be inserted between fields to enable this field alignment.

In some specific cases, the user is responsible for manually ensuring alignment [5].

[4] spec

3. Reordering the fields of a struct can change its size in memory

Different field orderings may require different amounts of padding.

4. Empty fields can take up memory

Go has the concept of empty, zero-size fields and structs, meaning that they take up no memory. Defined in the spec as:

A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero.

these empty fields are allocated 1 byte [12], when placed at the end of a struct. The alignment of the struct is calculated taking into account this extra byte.

These empty fields are commonly used to implement a Set using a Map, in the formmap[keyType]struct{}, which dedicates no memory to holding the values. [n2][13] describes the funny situation where, for a while, somewhere in the map implementation was one of these trailing empty structs, making this cool trick use as much memory as a map[int]bool.

5. Is there an optimal ordering in terms of memory usage?

Putting empty structs at the start and ordering the remaining fields by descending alignment yields the lowest memory usage. It's not a strict inequality, other orderings might take up the same amount of memory.

I've seen this 'rule' in many places, always stated without proof. It would be nice to derive it.

6. A smaller struct doesn't imply a smaller allocation

Go allocates memory in blocks of predetermined size. In Go 1.26, there are 67 possible sizes[6]. Reducing the size of a struct only results in a smaller allocation when the reduction crosses a size boundary. Roughly speaking, if it 'shrinks it into the previous size class'. Melatoni[7] goes over the functioning of the allocator.

7. Field order can affect performance

A notable example is false sharing. Several techniques are detailed in [8], [9], and [10].

8. Field order can affect the garbage collector

When marking, the garbage collector goes through a struct's pointers. By defining pointer types together and at the beginning, the garbage collector can scan ✨faster✨.

I did not find a fantastic source for what this means exactly, but it seems to be a common idea.

With the introduction of the Green Tea GC [11], whatever impact this optimization has now might change.

9. Can the compiler automatically do X?

There are several proposals to allow a struct definition to include compiler directives. Generically, these directives can be used to, for example:

Already in the language is the HostLayout directive, which has something to do with hinting at the compiler that it should do something funky with the alignment. I couldn't make out what that something is, and I read somewhere that for now, it is a no-op. Perhaps the language warms up to these directives embedded in structs, and we see more of them pop up in the future.

10. Tools

There are several tools to analyze structs in a Go codebase and identify which ones can use less memory.

Comments

Loading comments...