一、为何需要内存对齐?
1.平台(移植性)
不是所有的硬件平台都能够访问任意地址上的任意数据。例如:特定的硬件平台只允许在特定地址获取特定类型的数据,否则会导致异常情况
2.性能
若访问未对齐的内存,将会导致 CPU 进行两次内存访问,并且要花费额外的时钟周期来处理对齐及运算。而本身就对齐的内存仅需要一次访问就可以完成读取动作,这显然高效很多,是标准的空间换时间做法
二、内存对齐的基本原则
1.结构体的成员变量,第一个成员变量的偏移量为 0。往后的每个成员变量的对齐值必须为编译器默认对齐长度(#pragma pack(n))或 当前成员变量类型的长度 (unsafe. Sizeof ), 取最小值作为当前类型的对齐值 。其偏移量必须为对齐值的整数倍
2.结构体本身,对齐值必须为编译器默认对齐长度(#pragma pack(n))或 结构体的所有成员变量类型中的最大长度 ,取最大值的最小整数倍作为对齐值
3.结合以上两点,可得知若编译器默认对齐长度(#pragma pack(n))超过结构体内成员变量的类型最大长度时,默认对齐长度是没有任何意义的
在不同平台上的编译器都有自己默认的 “对齐系数”,可通过预编译命令 #pragma pack(n) 进行变更,n 就是代指 “对齐系数”。一般来讲,常用的平台的系数如下:
32 位:4
64 位:8
三、如何利用内存对齐来优化程序?
1.根据计算对齐值进行结构体成员之间顺序的调整,在一定程度上可以减少结构体占用的总内存
2.通过分析偏移量和对齐值,准确计算每个成员在结构体中的 偏移地址 (这里可以借助unsafe.Alignof和unsafe.Offsetof方法
四、 示例
type Test1 struct {
a bool
b int32
c int8
d int64
e byte
}
type Test2 struct {
e byte
c int8
a bool
b int32
d int64
}
func main() {
t1 := Test1{}
t2 := Test2{}
fmt. Printf (“t1 size: %d, align: %d\n”, unsafe.Sizeof(t1), unsafe.Alignof( t1))
fmt.Printf(“t2 size: %d, align: %d\n”, unsafe.Sizeof(t2), unsafe.Alignof(t2))
}
输出结果:
t1 size: 32, align: 8
t2 size: 16, align: 8
通过测试结果可以发现,调整了结构体成员变量的字段顺序,就减少了结构体的内存占用大小