在 golang 中优化 datastructure/word 对齐填充

Optimising datastructure/word alignment padding in golang

与我在 C++ 中学到的类似,我相信是填充导致了两个结构的实例大小不同。

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))
}

输出:

amd64
16
24

目前没有编译时优化;这些值在 x64 上被填充为 8 个字节。

您可以手动安排结构以最佳利用 space;通常是从较大的类型到较小的类型;例如 8 个连续的字节字段,将只使用 8 个字节,但单个字节将被填充到 8 字节对齐,考虑这个: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))
}

如果你考虑到这一点;您可以优化结构的内存占用。

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

是的,你应该。
这也称为 mechanical sympathy (see this Go Time podcast episode),因此它还取决于您要编译的硬件架构。

如图所示:

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

这取决于您正在开发的应用程序类型以及这些结构的使用情况。如果应用程序需要满足某些 memory/performance 标准,您绝对应该关心内存对齐和填充,但不仅如此 - 有一篇不错的文章 https://www.usenix.org/legacy/publications/library/proceedings/als00/2000papers/papers/full_papers/sears/sears_html/index.html 突出了最佳 CPU 缓存使用和相关性结构布局和性能。它突出了缓存行对齐、错误共享等。

还有一个不错的 golang 工具 https://github.com/1pkg/gopium 可以帮助自动执行这些优化,请查看!

一些指南

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

一个例外是空结构

我们知道 empty 的大小为零

type empty struct {
    a struct{}
}

按照上面的通用规则,我们可以将结构体的字段排列如下

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

但是E的大小是24,

将结构字段排列为

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

D的大小为16,参考https://go.dev/play/p/ID_hN1zwIwJ


IMO,最好使用帮助我们自动进行结构对齐优化的工具

linters-settings:
  maligned:
      # print struct with more effective memory layout or not, false by default
      suggest-new: true