golang Unsafe Alignof explained
概述
本文介绍 golang unsafe 包中的 Alignof 方法的用法,这对理解 unsafe 包中另一个方法 Sizeof 至关重要,let's go。
Go Alignof 的含义
对于任意表达式 x,当调用 Align(x) 的时候就相当于你定义了一个虚拟变量 v,然后将 x 赋值给 v,Alignof(x) 返回的值就是这个虚拟变量 v 的内存对齐要求。换句话说对齐 v 就是使: v 的地址 mod Alignof(v) == 0,同时 Alignof(v) 取最大可能的值。
那么按照上面的说法,如果 v 的地址是 100,那么最大的值是 100 吗?其实并不是这样,这个最大值是平台相关的,64 位系统,最大值为 64 / 8 = 8 字节,32 为系统为 4 字节。
对齐的目的是为了更高效的读取,CPU 总是以字(WORD)为单位读取,字的大小是平台相关的,64 位系统,字大小为 8 字节,32 位系统字大小为 4。试想去读一个整形变量,如果不对齐,那么该整形可能跨越两个字,需要两次读取,如果对齐了,只需要一次读取,因为一个整形的大小正好和字大小相同。
对齐保证
Go 语言规范中,只规定了以下最小的对齐保证:
- For a variable x of any type: unsafe.Alignof(x) is at least 1.
- For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x, but at least 1.
- For a variable x of array type: unsafe.Alignof(x) is the same as the alignment of a variable of the array's element type.
简单翻译就是:任何类型变量 x, 对齐要求至少为 1;对于 struct 类型的变量 x,x 中包含的字段为 f1 到 fn,那么 unsafe.Alignof(x) = max(unsafe.Alignof(x.f1), unsafe.Alignof(x.f2), ..., unsafe.Alignof(x.fn)),换句话说 struct 类型的变量 x 的对齐要求为 x 的所有字段的对齐要求的最大值;数组类型的变量 x 的对齐要求是由数组中元素类型的对齐要求决定的。
以下是 Go 标准编译器的对齐保证:
No. | type | 对齐保证 |
---|---|---|
1 | bool, uint8, int8 | 1 |
2 | uint16, int16 | 2 |
3 | uint32, int32 | 4 |
4 | float32, complex64 | 4 |
5 | arrays | 由元素类型决定 |
6 | structs | 由字段类型决定 |
7 | 其他类型 | 由平台 WORD 大小决定,即 64 位系统为 8,32 位系统为 4 |
No. 列是编号,下面的例子中会引用该编号以便让大家更好理解 Alignof 的输出依据的是哪条规则。
Golang Alignof 实战
注:以下例子均在 64 位操作系统上产生的结果。
输入 | Alignof | No. |
---|---|---|
int8(0) | 1 | 1 |
true | 1 | 1 |
int16(0) | 2 | 2 |
int32(0) | 4 | 3 |
int64(0) | 8 | 7 |
int(0) | 8 | 7 |
[2]int{0, 1} | 8 | 5 |
依据第 7 条规则,Alignof(int(0)) 输出结果为 8。需要注意的是:如果在 32 位系统上,输出结果为 4。
由于 Go 只规定了最小的对齐保证,大部分未规定类型的变量的 Alignof 要么是 8,要么是 4。因此下面的输出结果均为 8。需要注意 []int8 {1}
是切片,不是数组,所以不能依据规则 5,而是应该依据规则 7。
// 8
println(unsafe.Alignof(""))
// 8
println(unsafe.Alignof("abc"))
// 8
println(unsafe.Alignof([]int8 {1}))
// 1
println(unsafe.Alignof([1]int8 {1}))
结构体的 Alignof 稍微复杂,结构体的对齐取决于所有字段对齐保证的最大值,套下面的公式即可:
unsafe.Alignof(x) = max(unsafe.Alignof(x.f1), unsafe.Alignof(x.f2), ..., unsafe.Alignof(x.fn))
假设一个结构体定义如下:
type M struct {
x int8
y int32
z int64
}
m := M{}
// 输出 8
println(unsafe.Alignof(m))
unsafe.Alignof(m) = max(unsafe.Alignof(m.x), unsafe.Alignof(m.y), unsafe.Alignof(m.z)) = max(1, 4, 8) = 8
总结
Golang Alignof 并不复杂,只要记住最小对齐保证即可。
温馨提示:反馈需要登录