這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
最近在做一些效能最佳化的工作,其中有個結構體佔用的空間比較大,而且在記憶體中的數量又特別多,就在想有沒有最佳化的空間,想起了 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 int8type I16 int16type I32 int32So(unsafe.Sizeof(struct { i8 I8 i16 I16 i32 I32}{}), ShouldEqual, 8)
給類型重新命名之後,類型的大小並沒有發生改變
轉載請註明出處
本文連結:http://hatlonely.com/2018/03/...