在 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
- 定义结构成员时是否有可遵循的经验法则? (比如 ascending/descending 类型大小的顺序)
- 是否有我们可以通过的编译时优化,可以自动解决这个问题?
- 或者我根本不应该担心这个吗?
目前没有编译时优化;这些值在 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 day byte alignment came back to bite me"(2014 年 1 月)
- "On the memory alignment of Go slice values"(2016 年 7 月)
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,最好使用帮助我们自动进行结构对齐优化的工具
- 对齐检查 — https://gitlab.com/opennota/check
- 中伤 — https://github.com/mdempsky/maligned
- golang 克林特
- 您只需要在“golangci-lint”设置中启用“恶意”即可。
例如,来自配置文件
.golangci.example.yml
linters-settings:
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
与我在 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
- 定义结构成员时是否有可遵循的经验法则? (比如 ascending/descending 类型大小的顺序)
- 是否有我们可以通过的编译时优化,可以自动解决这个问题?
- 或者我根本不应该担心这个吗?
目前没有编译时优化;这些值在 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 day byte alignment came back to bite me"(2014 年 1 月)
- "On the memory alignment of Go slice values"(2016 年 7 月)
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,最好使用帮助我们自动进行结构对齐优化的工具
- 对齐检查 — https://gitlab.com/opennota/check
- 中伤 — https://github.com/mdempsky/maligned
- golang 克林特
- 您只需要在“golangci-lint”设置中启用“恶意”即可。
例如,来自配置文件
.golangci.example.yml
- 您只需要在“golangci-lint”设置中启用“恶意”即可。
例如,来自配置文件
linters-settings:
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true