内存对齐
内存对齐是为了cpu更高效访问内存中数据
struct的对齐是:如果类型 t 的对齐保证是 n,那么类型 t 的每个值的地址在运行时必须是 n 的倍数。uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
struct内字段如果填充过多,可以尝试重排,使字段排列更紧密,减少内存浪费
零大小字段要避免作为struct最后一个字段,会有内存浪费
32位系统上对64位字的原子访问要保证其是8bytes对齐的;当然如果不必要的话,还是用加锁(mutex)的方式更清晰简单
如果WaitGroup嵌套到别的结构体时,如果不放到结构体首位会有问题, 这会使其使用受限
合理的字段顺序使struct更紧密,更省内存
type T1 struct {
a [2]int8
b int64
c int16
}
type T2 struct {
a [2]int8
c int16
b int64
}
fmt.Printf("arrange fields to reduce size:\n"+
"T1 align: %d, size: %d\n"+
"T2 align: %d, size: %d\n",
unsafe.Alignof(T1{}), unsafe.Sizeof(T1{}),
unsafe.Alignof(T2{}), unsafe.Sizeof(T2{}))
/*
output:
arrange fields to reduce size:
T1 align: 8, size: 24
T2 align: 8, size: 16
*/
/*
以64位系统为例,分析如下:
T1,T2内字段最大的都是int64, 大小为8bytes,对齐按机器字确定,64位下是8bytes,所以将按8bytes对齐
T1.a 大小2bytes,填充6bytes使对齐(后边字段已对齐,所以直接填充)
T1.b 大小8bytes,已对齐
T1.c 大小2bytes,填充6bytes使对齐(后边无字段,所以直接填充)
总大小为 8+8+8=24
T2中将c提前后,a和c总大小4bytes,在填充4bytes使对齐
总大小为 8+8=16
*/
零大小字段不要放在最后
type T1 struct {
a struct{}
x int64
}
type T2 struct {
x int64
a struct{}
}
a1 := T1{}
a2 := T2{}
fmt.Printf("zero size struct{} in field:\n"+
"T1 (not as final field) size: %d\n"+
"T2 (as final field) size: %d\n",
// 8
unsafe.Sizeof(a1),
// 64位:16;32位:12
unsafe.Sizeof(a2))
内存地址对齐
unsafe包规范中说明如果类型t的对齐保证是n,那么类型t的每个值的地址在运行时必须是n的倍数
一个典型示例是
type WaitGroup struct {
noCopy noCopy
state1 [3]uint32
}
// state returns pointers to the state and sema fields stored within wg.state1.
func (wg *WaitGroup) state() (statep *uint64, semap *uint32) {
// 判定地址是否8位对齐
if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {
// 前8bytes做uint64指针statep,后4bytes做sema
return (*uint64)(unsafe.Pointer(&wg.state1)), &wg.state1[2]
} else {
// 后8bytes做uint64指针statep,前4bytes做sema
return (*uint64)(unsafe.Pointer(&wg.state1[1])), &wg.state1[0]
}
}
如果是8位地址对齐前8bytes做uint64指针statep,后4bytes做sema 如果不是8位地址对齐(4为地址对齐)则选择前4byte做sema,后8字节做uint64指针statep
可是为什么要在32位系统上也要保证一个64位对齐的uint64指针呢? 答案是,为了保证在32位系统上也能原子访问64位对齐的64位字。
64位字安全访问保证
而32位系统,4byte对齐,字长也为4bytes,可能出现uint64的数据分布在两个数据块中,需要两次操作才能完成访问。
如果两次操作中间有可能别其他操作修改,不能保证原子性。
变量或开辟的结构体、数组和切片值中的第一个64位字可以被认为是8字节对齐
参考
最后更新于
这有帮助吗?