詳解簡單高效的Go struct優化

前言

結構體的定義,大傢都很熟悉,但想要定義出更節省內存空間的結構體,可不是一件簡單的事。

我們必須掌握瞭Go的結構體內存對齊機制,才能做出相應的優化(節省內存並提高性能)。

先來看個例子

下面定義兩個結構體,字段都一樣,隻是部分字段稍微調整瞭一下順序。

但輸出的結果,為什麼bad占用24字節,而good卻隻占用16字節呢?一個順序調整就節省瞭8個字節,太神奇瞭

type BadSt struct {
  A int32
  B int64
  C bool
}

type GoodSt struct {
  A int32
  C bool
  B int64
}

func main() {
  bad := BadSt{A: 10, B: 20, C: false}
  fmt.Println(unsafe.Sizeof(bad))
  good := GoodSt{A: 10, B: 20, C: false}
  fmt.Println(unsafe.Sizeof(good))
}
//輸出結果:
//24
//16

想要解開這個問題,我們得先來學習一下內存對齊機制,然後再來進一步分析

內存對齊機制

  • 基本概念

為瞭能讓CPU可以更快的存儲與讀取到各個字段,Go編譯器會幫我們把結構體做數據的對齊。所謂的數據對齊,是指內存地址是所存儲數據大小的整數倍(按字節為單位),以便CPU可以一次將該數據從內存中讀取出來,減少瞭讀取次數。編譯器通過在結構體的各個字段之間填充一些空白已達到對齊的目的

  • CPU訪問內存

CPU 訪問內存時,並不是逐個字節訪問,而是以機器字(word)為單位進行訪問。比如 64位CPU的字長(word size)為8bytes,那麼CPU訪問內存的單位也是8字節,每次加載的內存數據也是固定的若幹字長,如8words(64bytes)、16words(128bytes)等

  • 對齊系數

不同硬件平臺占用的大小和對齊值都可能是不一樣的,每個特定平臺上的編譯器都有自己的默認"對齊系數",32位系統對齊系數是4,64位系統對齊系數是8

不同類型的對齊系數也可能不一樣,使用Go語言中的unsafe.Alignof函數可以返回相應類型的對齊系數,對齊系數都符合2^n這個規律,最大也不會超過8

func main() {
  fmt.Printf("bool:   %d\n", unsafe.Alignof(bool(true)))
  fmt.Printf("string: %d\n", unsafe.Alignof(string("a")))
  fmt.Printf("int:    %d\n", unsafe.Alignof(int(0)))
  fmt.Printf("int32:  %d\n", unsafe.Alignof(int32(0)))
  fmt.Printf("int64:  %d\n", unsafe.Alignof(int64(0)))
  fmt.Printf("float64:  %d\n", unsafe.Alignof(float64(0)))
  fmt.Printf("float32:%d\n", unsafe.Alignof(float32(0)))
}
//輸出結果:
//bool:   1
//string: 8
//int:    8
//int32:  4
//int64:  8
//float64:8
//float32:4

  • 對齊原則
  • 結構體變量中成員的偏移量必須是成員大小的整數倍
  • 整個結構體的內存大小必須是最大字節的整數倍(結構體的內存占用是1/4/8/16byte…)

案例進一步分析

  • BadSt結構體,占用24個字節

type BadSt struct {
  A int32
  B int64
  C bool
}

分析過程:

  • 字段A 4字節:先計算偏移量,最開頭下標為0,0%4=0,正好整除,先占用4個字節;
  • 字段B 8字節:下標4-7,對8都不能整除,則填充空白,下標8可以整除,所以下標8-15 8個字節為字段B的存儲使用;
  • 字段C 1字節:下標16,對1可以整除,所以下標16則用作字段C的存儲;
  • 最後,該結構體字段最大字節為8且目前已占用17字節,要保證是整數倍,所以最後面需要填充7個字節,占滿24字節,才能滿足條件(對齊原則2)。
  • GoodSt結構體,占用16個字節

type GoodSt struct {
  A int32
  C bool
  B int64
}

分析過程:

  • 字段A 4字節:先計算偏移量,最開頭下標為0,0%4=0,正好整除,先占用4個字節;
  • 字段C 1字節:下標4,對1可以整除,所以下標4則用作字段C的存儲;
  • 字段B 8字節:下標5-7,對8都不能整除,則填充空白,下標8可以整除,所以下標8-15 8個字節為字段B的存儲使用;
  • 最後,該結構體字段最大字節為8且目前已占用16字節,正好是整數倍,所以後面不再需要填充空白瞭。

總結

掌握瞭內存對齊機制後,結構體struct的優化,是不是也會覺得原來如此簡單高效呀,調整下字段順序,效果立竿見影(簡單理解,就是將對齊系數小的字段,盡可能的放在一起)。

內存對齊其實就是典型的空間換時間的方式,來達到優化的目的。牢記對齊原則,對實際場景進行分析,一切都會很變得清晰明瞭。

以上就是詳解簡單高效的Go struct優化的詳細內容,更多關於Go struct優化的資料請關註WalkonNet其它相關文章!

推薦閱讀: