博客资讯
市场瞬息万变,营销困难重重,资讯让我们决策更加科学,知识让我们付出更加高效。

编写内存高效且 CPU 优化的 Go Structs

2022-05-05 21:59:27
nbvghost
5

结构是字段的类型化集合,可用于将数据分组到记录中。 这允许与一个实体相关的所有数据被巧妙地封装在一个轻量级类型定义中,然后可以通过在结构类型上定义函数来实现行为。


这篇文章我将尝试解释我们如何根据内存使用和 CPU 周期有效地编写结构。


让我们考虑下面的这个结构,为展示一些奇怪的用例,定义 TerraformResource 类型:

type TerraformResource struct {
  Cloud                string                       // 16 bytes
  Name                 string                       // 16 bytes
  HaveDSL              bool                         //  1 byte
  PluginVersion        string                       // 16 bytes
  IsVersionControlled  bool                         //  1 byte
  TerraformVersion     string                       // 16 bytes
  ModuleVersionMajor   int32                        //  4 bytes
}


让我们使用以下代码查看 TerraformResource 结构需要多少内存分配


package main

import "fmt"
import "unsafe"

type TerraformResource struct {
  Cloud                string                       // 16 bytes
  Name                 string                       // 16 bytes
  HaveDSL              bool                         //  1 byte
  PluginVersion        string                       // 16 bytes
  IsVersionControlled  bool                         //  1 byte
  TerraformVersion     string                       // 16 bytes
  ModuleVersionMajor   int32                        //  4 bytes
}

func main() {
    var d TerraformResource
    d.Cloud = "aws"
    d.Name = "ec2"
    d.HaveDSL = true
    d.PluginVersion = "3.64"
    d.TerraformVersion = "1.1"
    d.ModuleVersionMajor = 1
    d.IsVersionControlled = true
    fmt.Println("==============================================================")
    fmt.Printf("Total Memory Usage StructType:d %T => [%d]\n", d, unsafe.Sizeof(d))
    fmt.Println("==============================================================")
    fmt.Printf("Cloud Field StructType:d.Cloud %T => [%d]\n", d.Cloud, unsafe.Sizeof(d.Cloud))
    fmt.Printf("Name Field StructType:d.Name %T => [%d]\n", d.Name, unsafe.Sizeof(d.Name))
    fmt.Printf("HaveDSL Field StructType:d.HaveDSL %T => [%d]\n", d.HaveDSL, unsafe.Sizeof(d.HaveDSL))
    fmt.Printf("PluginVersion Field StructType:d.PluginVersion %T => [%d]\n", d.PluginVersion, unsafe.Sizeof(d.PluginVersion))
    fmt.Printf("ModuleVersionMajor Field StructType:d.IsVersionControlled %T => [%d]\n", d.IsVersionControlled, unsafe.Sizeof(d.IsVersionControlled))
    fmt.Printf("TerraformVersion Field StructType:d.TerraformVersion %T => [%d]\n", d.TerraformVersion, unsafe.Sizeof(d.TerraformVersion))
    fmt.Printf("ModuleVersionMajor Field StructType:d.ModuleVersionMajor %T => [%d]\n", d.ModuleVersionMajor, unsafe.Sizeof(d.ModuleVersionMajor))  
}


输出:

>> go run golang-struct-memory-allocation.go==============================================================
Total Memory Usage StructType:d main.TerraformResource => [88]
==============================================================
Cloud Field StructType:d.Cloud string => [16]
Name Field StructType:d.Name string => [16]
HaveDSL Field StructType:d.HaveDSL bool => [1]
PluginVersion Field StructType:d.PluginVersion string => [16]
ModuleVersionMajor Field StructType:d.IsVersionControlled bool => [1]
TerraformVersion Field StructType:d.TerraformVersion string => [16]
ModuleVersionMajor Field StructType:d.ModuleVersionMajor int32 => [4]

因此 TerraformResource 结构所需的总内存分配为 88 个字节。 这是 TerraformResource 类型的内存分配的样子


但是 88 个字节,16 +16 + 1 + 16 + 1+ 16 + 4 = 70 个字节怎么来的,这额外的 18 个字节是从哪里来的呢?


当涉及到结构的内存分配时,它们总是被分配连续的、字节对齐的内存块,并且按照定义的顺序分配和存储字段。 在这种情况下,字节对齐的概念意味着连续的内存块以等于平台字大小的偏移量对齐。

我们可以清楚地看到 TerraformResource.HaveDSL 、 TerraformResource.isVersionControlled 和 TerraformResource.ModuleVersionMajor 分别只占用 1 Byte、1Byte 和 4 Bytes。 其余空间被空填充字节填充。


现在重新计算一下占用字节

分配字节 = 16 字节 + 16 字节 + 16 字节 + 16 字节 +4 字节 + 1 字节 + 1 字节 = 70 字节
空填充字节 = 2 个字节
总字节 = 分配字节 + 空填充字节 = 70 字节 + 2 字节 = 72 字节


正确的数据结构对齐不仅可以帮助我们有效地使用内存,还可以帮助我们提高 CPU 读取周期......如何?

CPU 以字为单位读取内存,在 32 位系统上为 4 个字节,在 64 位系统上为 8 个字节。 现在我们第一次声明结构类型 TerraformResource 需要 11 个字让 CPU 读取所有内容

然而,优化后的结构将只占用 9 个单词,如下所示

通过正确定义数据结构对齐的结构,我们能够有效地使用内存分配,并使结构在 CPU 读取方面快速高效。


这只是一个小例子,想想一个有 20 或 30 个不同类型字段的大型结构。 数据结构的深思熟虑的对齐真的得到了回报......🤩


希望这个文章能够阐明结构内部结构、它们的内存分配和所需的 CPU 读取周期。 希望这可以帮助到你!!

Share this post:
QQ咨询
微信咨询
免费咨询
定制开发

定制产品咨询

定制版面

定制解决方案

联系架构师
如您无需以上服务,请前往免费咨询