优化golang中数据结构/单词对齐填充

30

和我在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
  • 在定义结构成员时是否有遵循的经验法则?(例如按类型大小升序/降序排列)
  • 是否有一种可以传递的编译时优化,可以自动处理这个问题?
  • 或者我根本不需要担心这个问题吗?

2
我制作了一个示例,可能会提供一些见解 https://play.golang.org/p/dNWspo2Dxv - jpgerek
4个回答

26

目前没有编译时优化。在x64上,这些值会填充到8字节。

您可以手动排列结构体以最大限度地利用空间;通常是从大型类型到小型类型;例如,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))
}
如果您考虑这一点,您可以优化结构的内存占用。

3
我还想补充一点,你可以使用像structlayoutaligncheck这样的工具来可视化你的结构体,帮助你优化它们的布局。 - Chewxy

17

我也试图修复2014年的链接,但是网站kernel-panic.runkite.com似乎已经消失了。 - Arnaud P
1
@ArnaudP 感谢您的编辑。我也已经恢复了 kernel-panic.runkite.com 的链接。 - VonC

2
这取决于您正在开发的应用程序类型以及这些结构的使用情况。如果应用程序需要满足某些内存/性能标准,您肯定应该关注内存对齐和填充,但不仅如此-有一篇很好的文章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可以帮助自动化这些优化,试试吧!

2

一些指南

为了最小化填充字节的数量,我们必须从最高分配到最低分配来布局字段。

唯一的例外是一个空结构体

正如我们所知,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


我认为最好使用工具来帮助我们自动化结构对齐优化。

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

1
"golang clint"非常适合我的使用情况,感谢您的建议。 - nohup
1
https://github.com/mdempsky/maligned 已经被弃用。建议使用 https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignment 代替。 - Vasiliy Toporov
1
最后但并非最不重要的,这里是如何使用fieldalignment对项目中的所有结构进行重新排序的方法。https://github.com/golangci/golangci-lint/discussions/2298#discussioncomment-1631196 - Vasiliy Toporov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接