Golang 简单的数据对齐可提高程序速度和内存使用率
序
Golang 中的结构或 struct 是用户定义的类型,允许将可能不同类型的项分组/组合为单一类型。
可以说是一个不支持继承但支持组合的轻量级类。
我们使用 Golang 编写代码的时候,你肯定使用过struct
。
但是,你可能不知道的是,通过简单地重新排序结构中的字段,可以极大地提高 Go 程序的速度和内存使用率!
示例演示
type EmployeeStruct struct {
IsPublic bool
Age int64
Status bool
Name string
Image float32
}
我们来看一下Employee结构体的内存大小:
- IsPublic(boolean) 1 字节
- Age(int64) 8 字节
- Status(boolean) 1 字节
- Name(string) 16 字节
- Image(float32) 4 字节
总计: 30 字节
通过unsafe.Sizeof
检查一下:
package main
import (
"fmt"
"unsafe"
)
type EmployeeStruct struct {
IsPublic bool
Age int64
Status bool
Name string
Image float32
}
func main() {
var employee EmployeeStruct
fmt.Println(unsafe.Sizeof(employee))
}
执行后输出: 48字节,为什么呢?
处理器类型
我们知道CPU 分为32位、34位,但是,它是如何运作的呢?
想象一下我们有一个 64 位的 CPU,每个时钟周期传输 64 位数据的能力。
时钟周期是CPU处理一个信息需要多少时间,CPU 32位在1个周期内转换
4字节数据
,CPU 64位在1个周期内转换8字节数据(32位= 4字节,64位= 8字节)
上面我们定义的EmployeeStruct
结构体,并且计算得到了每个字段在内存中占用的字节数。下面我们看CPU处理信息需要多少时间呢?
先来看下面的一张图:
图里面6个周期,每个周期有八个盒子,表示是CPU处理能力,为 8 字节
-
周期1
第一个属性是IsPublic
,数据类型为布尔型
,bool
的大小为1 个字节
。因此,当前周期 1 由属性IsPublic(bool ~ 1 byte)填充。
下一个属性是Age
,数据类型为int64
,int64
的大小是8字节
,由于第1个周期的剩余内存大小只有7个字节,因此第1个周期无法填充Age
属性,因此,会进入第2个周期,但是,“剩余的空间呢,怎么办?”。 -
周期2
第2个周期由Age
属性填充,数据类型为int64
,大小为8字节
,刚好填充满。 -
周期3
第三个属性Status
,数据类型为bool
,大小为1字节
,填充(bool~1 byte),剩余内存大小为7字节
,然后,下一个属性是Name
,类型为字符串
,大小为16 字节
,与周期1的情况相同,因为没有足够的空间容纳下一个属性,所以将在下一个周期中进入。所以意味着有7个字节的内存被浪费了
。 -
周期 4 和周期 5
第四个是Name
,数据类型为字符串
,大小为16字节
。从图上面看,它将在两个周期中填充,周期 4 中填充 8 个字节,周期 5 中填充 8 个字节。
-
周期6
最后一个属性是Image
, 数据类型为float32
,大小为4字节
,浪费剩余的4 个字节
。
最后通过计算统计:
总时钟周期 = 6 个时钟周期
结构体大小 = 48 字节
浪费的总内存 = 18 字节
所以我们在开发的时候,如果对于一个比较大的数据结构体来说,可能会使结构体的大小变得更大。
那么,如何解决呢?
实际上我们可以根据数据类型的大小来组成序列,最简单的方法是:
按元素内存大小的降序排列字段
type EmployeeStruct struct {
Name string
Age int64
Image float32
IsPublic bool
Status bool
}
这个时候我们再来看一下时间周期:
-
周期 1 和周期 2
第一个属性是Name
,数据类型为字符串,大小为16字节。因此,它将在两个周期中填充,周期 1 中填充 8 个字节,周期 2 中填充 8 个字节。 -
周期3
第3个周期由Age
属性填充,数据类型为int64
,大小为8字节 -
周期4
下一个属性是Image
,数据类型为float32
,大小为4字节。因此,当前周期 4 由属性Image ( float32 ~ 4 字节)
填充,剩余大小为 4 字节。
下一个属性是IsPublic
(bool ~ 1 字节)和 Status(bool ~ 1 字节)。因为最后两个属性的总大小只有2个字节
,所以我们可以将其放在循环4中。这样一来,循环4就被Image ( float32 ~ 4字节)、IsPublic (bool ~ 1字节)、Status(bool~1 字节)填满了,仅浪费 2 字节内存。
最后通过计算统计:
总时钟周期 = 4 个时钟周期
结构体大小 = 32 字节
浪费的总内存 = 2 字节
package main
import (
"fmt"
"unsafe"
)
type EmployeeStruct struct {
Name string
Age int64
Image float32
IsPublic bool
Status bool
}
func main() {
var employee EmployeeStruct
fmt.Println(unsafe.Sizeof(employee)) // output: 32
}
结束
所以我们在开发的时候,重新排序结构字段是可以提高应用程序的内存使用率和运行速度的。
转载:风向阅读 - Golang 开发技巧 - 简单的数据对齐可提高程序速度和内存使用率
地址:https://www.aiweimeng.top/archives/56.html