struct
结构体的字段顺序有什么讲究?您可能不知道,通过简单地重新排序结构体中的字段,可以极大地提高 Go 程序的速度和内存使用率!难以置信吧?让我们来看个例子。
ep:
type BadStruct struct {
age uint8
IdCardNumber uint64
DateOfBirth uint16
}
type GoodStruct struct {
age uint8
DateOfBirth uint16
IdCardNumber uint64
}
在上面的例子中,我们定义了两个具有相同字段的结构体。难以想象他们创建出来的实体不一样大!测试:
func main() {
var bad BadStruct
var good GoodStruct
fmt.Printf("Bad struct is %v bytes long\n", unsafe.Sizeof(bad))
fmt.Printf("Good struct is %v bytes long", unsafe.Sizeof(good))
}
输出:
Bad struct is 24 bytes long
Good struct is 16 bytes long
居然多了8
个byte
,到底是道德的沦丧,还是人性的扭曲?导致两个字段相同的结构体消耗不同的字节?
答案是数据在操作系统中的内存排列方式。换句话说,内存对齐。
★
CPU
以字长的方式读取数据,而不是通过字节大小。64 位操作系统中一个字长为 8 个字节,而 32 位操作系统中一个字长为 4 个字节。换句话说,CPU
以字长的倍数读取地址。 ”
一张调皮的内存对齐示意图
1byte
=8bit
,64位系统是64bit
=8byte
。每次CPU
访问八个字节的数据。uint8
为1byte
占一个格子,占了一个字节,剩下七个。IdCardNumber
有八个字节,所以CPU
需要两个周期来访问数据,而不是一个周期。这是低效的。
所以很多语言都会自动对齐数据,-- 将数据存储在一个地址等于数据大小的倍数的位置。如图:
数据对齐后
这样就能确保 IdCardNumber
可以在同一个 CPU
周期内检索到变量。也正是因为填充了额外的空白数据导致内存膨胀!
对于最初的两个struct
,只需要尽可能紧凑,让多个小字段挤在8byte
里。如下图:
image.png
GoodStruct
消耗更少的内存,仅仅是因为它比 BadStruct
有更好的结构体字段顺序。
由于填充,两个数据结构分别变成了 16
字节和 24
字节。少了8
个字节。
所以,只需重新排序结构体中的字段,就可以节省额外的内存!
来做一个基准测试来证明它在速度和内存的区别结果如下。
基准测试代码
从结果可以看出,遍历 GoodStruct
花费的时间确实更少。重新排序结构体字段可以提高程序的内存使用率和速度。
国玮在网上找到了两个项目可能和自动校准内存对齐有关,有兴趣可以测测看哦!~
本次了解了简单的数据对齐技术(内存对齐),重新排序结构体中你的字段吧! 数据结构的深思熟虑的对齐真的得到了回报。