golang 位元組對齊

hatlonely發表於2018-03-17

最近在做一些效能優化的工作,其中有個結構體佔用的空間比較大,而且在記憶體中的數量又特別多,就在想有沒有優化的空間,想起了 c 語言裡面的位元組對齊,通過簡單地調整一下欄位的順序,就能省出不少記憶體,這個思路在 golang 裡面同樣適用

基本資料大小

在這之前先來看下 golang 裡面基本的型別所佔資料的大小

So(unsafe.Sizeof(true), ShouldEqual, 1)
So(unsafe.Sizeof(int8(0)), ShouldEqual, 1)
So(unsafe.Sizeof(int16(0)), ShouldEqual, 2)
So(unsafe.Sizeof(int32(0)), ShouldEqual, 4)
So(unsafe.Sizeof(int64(0)), ShouldEqual, 8)
So(unsafe.Sizeof(int(0)), ShouldEqual, 8)
So(unsafe.Sizeof(float32(0)), ShouldEqual, 4)
So(unsafe.Sizeof(float64(0)), ShouldEqual, 8)
So(unsafe.Sizeof(""), ShouldEqual, 16)
So(unsafe.Sizeof("hello world"), ShouldEqual, 16)
So(unsafe.Sizeof([]int{}), ShouldEqual, 24)
So(unsafe.Sizeof([]int{1, 2, 3}), ShouldEqual, 24)
So(unsafe.Sizeof([3]int{1, 2, 3}), ShouldEqual, 24)
So(unsafe.Sizeof(map[string]string{}), ShouldEqual, 8)
So(unsafe.Sizeof(map[string]string{"1": "one", "2": "two"}), ShouldEqual, 8)
So(unsafe.Sizeof(struct{}{}), ShouldEqual, 0)
  • bool 型別雖然只有一位,但也需要佔用 1 個位元組,因為計算機是以位元組為單位
  • 64 為的機器,一個 int 佔 8 個位元組
  • string 型別佔 16 個位元組,內部包含一個指向資料的指標(8 個位元組)和一個 int 的長度(8 個位元組)
  • slice 型別佔 24 個位元組,內部包含一個指向資料的指標(8 個位元組)和一個 int 的長度(8 個位元組)和一個 int 的容量(8 個位元組)
  • map 型別佔 8 個位元組,是一個指向 map 結構的指標
  • 可以用 struct{} 表示空型別,這個型別不佔用任何空間,用這個作為 map 的 value,可以講 map 當做 set 來用

位元組對齊

結構體中的各個欄位在記憶體中並不是緊湊排列的,而是按照位元組對齊的,比如 int 佔 8 個位元組,那麼就只能寫在地址為 8 的倍數的地址處,至於為什麼要位元組對齊,主要是為了效率考慮,而更深層的原理看了一下網上的說法,感覺不是很靠譜,就不瞎說了,感興趣可以自己研究下

// |x---|
So(unsafe.Sizeof(struct {
    i8 int8
}{}), ShouldEqual, 1)

簡單封裝一個 int8 的結構體,和 int8 一樣,僅佔 1 個位元組,沒有額外空間

// |x---|xxxx|xx--|
So(unsafe.Sizeof(struct {
    i8  int8
    i32 int32
    i16 int16
}{}), ShouldEqual, 12)

// |x-xx|xxxx|
So(unsafe.Sizeof(struct {
    i8  int8
    i16 int16
    i32 int32
}{}), ShouldEqual, 8)

這兩個結構體裡面的內容完全一樣,調整了一下欄位順序,節省了 33% 的空間

// |x---|xxxx|xx--|----|xxxx|xxxx|
So(unsafe.Sizeof(struct {
    i8  int8
    i32 int32
    i16 int16
    i64 int64
}{}), ShouldEqual, 24)

// |x-xx|xxxx|xxxx|xxxx|
So(unsafe.Sizeof(struct {
    i8  int8
    i16 int16
    i32 int32
    i64 int64
}{}), ShouldEqual, 16)

這裡需要注意的是 int64 只能出現在 8 的倍數的地址處,因此第一個結構體中,有連續的 4 個位元組是空的

type I8 int8
type I16 int16
type I32 int32

So(unsafe.Sizeof(struct {
    i8  I8
    i16 I16
    i32 I32
}{}), ShouldEqual, 8)

給型別重新命名之後,型別的大小並沒有發生改變

> 轉載請註明出處 > 本文連結:<http://hatlonely.com/2018/03/17/golang-%E5%AD%97%E8%8A%82%E5%AF%B9%E9%BD%90/>

更多原創文章乾貨分享,請關注公眾號
  • golang 位元組對齊
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章