29

Similar to what I've learned in C++, I believe it's the padding that causes a difference in the size of instances of both structs.

type Foo struct {
    w byte //1 byte
    x byte //1 byte
    y uint64 //8 bytes
}
type Bar struct {
    x byte //1 byte
    y uint64 //8 bytes
    w byte// 1 byte
}
func main() {
    fmt.Println(runtime.GOARCH)
    newFoo := new(Foo)
    fmt.Println(unsafe.Sizeof(*newFoo))
    newBar := new(Bar)
    fmt.Println(unsafe.Sizeof(*newBar))
}

Output:

amd64
16
24
  • Is there a rule of thumb to follow when defining struct members? (like ascending/descending order of size of types)
  • Is there a compile time optimisation which we can pass, that can automatically take care of this?
  • Or shouldn't I be worried about this at all?
nohup
  • 3,105
  • 3
  • 27
  • 52

4 Answers4

26

Currently there's no compile-time optimisation; the values are padded to 8 bytes on x64.

You can manually arrange structs to optimally utilise space; typically by going from larger types to smaller; 8 consecutive byte fields for example, will only use 8 bytes, but a single byte would be padded to an 8 byte alignment, consider this: https://play.golang.org/p/0qsgpuAHHp

package main

import (
    "fmt"
    "unsafe"
)

type Compact struct {
    a, b                   uint64
    c, d, e, f, g, h, i, j byte
}

// Larger memory footprint than "Compact" - but less fields!
type Inefficient struct {
    a uint64
    b byte
    c uint64
    d byte
}

func main() {
    newCompact := new(Compact)
    fmt.Println(unsafe.Sizeof(*newCompact))
    newInefficient := new(Inefficient)
    fmt.Println(unsafe.Sizeof(*newInefficient))
}

If you take this into consideration; you can optimise the memory footprint of your structs.

Martin Gallagher
  • 4,444
  • 2
  • 28
  • 26
  • 3
    I'd also like to add that you can visualize your struct by using tools like [structlayout](https://github.com/dominikh/go-structlayout) and [aligncheck](https://github.com/opennota/check) to help you optimize your layout of structs. – Chewxy Sep 26 '16 at 07:21
17

Or shouldn't I be worried about this at all?

Yes you should.
This is also called mechanical sympathy (see this Go Time podcast episode), so it also depends on the hardware architecture you are compiling for.

See as illustration:

The values in Go slices are 16-byte aligned. They are not 32 byte aligned.
Go pointers are byte-aligned.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • I tried to fix the 2014 link as well, but site kernel-panic.runkite.com seems to be gone. – Arnaud P May 06 '21 at 15:28
  • 1
    @ArnaudP Thank you for your edit. I have restored the kernel-panic.runkite.com link as well. – VonC May 06 '21 at 15:45
2

It depends on type of application that you are developing and on usage of those structures. If application needs to meet some memory/performance criteria you definitely should care about memory alignment and paddings, but not only - there is nice article https://www.usenix.org/legacy/publications/library/proceedings/als00/2000papers/papers/full_papers/sears/sears_html/index.html that highlights theme of optimal CPU caches usage and correlation between struct layouts and performance. It highlights cache line alignment, false sharing, etc.

Also there is a nice golang tool https://github.com/1pkg/gopium that helps to automate those optimizations, check it out!

John Brown
  • 21
  • 1
2

Some guideline

To minimize the number of padding bytes, we must lay out the fields from the highest allocation to lowest allocation.

One exception is an empty structure

As we know the size of empty is zero

type empty struct {
    a struct{}
}

Following the common rule above, we may arrange the fields of structure as below

type E struct {
    a int64
    b int64
    c struct{}
}

However, the size of E is 24,

When arrange the fields of structure as

type D struct {
    b struct{}
    a int64
    c int64
}

The size of D is 16, refer to https://go.dev/play/p/ID_hN1zwIwJ


IMO, it is better to use tools that help us to automate structure alignment optimizations

linters-settings:
  maligned:
      # print struct with more effective memory layout or not, false by default
      suggest-new: true
zangw
  • 43,869
  • 19
  • 177
  • 214
  • 1
    `golang clint` is perfect for my use cases, thank you for suggesting this. – nohup Mar 26 '22 at 10:42
  • 1
    https://github.com/mdempsky/maligned is deprecated. Suggest to use https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignment instead. – Vasiliy Toporov Oct 11 '22 at 16:23
  • 1
    And last, but not least. Here how you can use **fieldalignment** to resort all the structures in the project. https://github.com/golangci/golangci-lint/discussions/2298#discussioncomment-1631196 – Vasiliy Toporov Oct 12 '22 at 08:53