首頁 > 軟體

詳解簡單高效的Go struct優化

2023-03-07 06:01:38

前言

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

我們必須掌握了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:   %dn", unsafe.Alignof(bool(true)))
  fmt.Printf("string: %dn", unsafe.Alignof(string("a")))
  fmt.Printf("int:    %dn", unsafe.Alignof(int(0)))
  fmt.Printf("int32:  %dn", unsafe.Alignof(int32(0)))
  fmt.Printf("int64:  %dn", unsafe.Alignof(int64(0)))
  fmt.Printf("float64:  %dn", unsafe.Alignof(float64(0)))
  fmt.Printf("float32:%dn", 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優化的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com