### 序 Golang 中的結構或 struct 是用戶定義的類型,允許將可能不同類型的項分組/組合為單一類型。 可以說是一個不支持繼承但支持組合的輕量級類。 我們使用 Golang 編寫代碼的時候,你肯定使用過`struct`。 但是,你可能不知道的是,通過簡單地重新排序結構中的欄位,可以極大地 ...
序
Golang 中的結構或 struct 是用戶定義的類型,允許將可能不同類型的項分組/組合為單一類型。
可以說是一個不支持繼承但支持組合的輕量級類。
我們使用 Golang 編寫代碼的時候,你肯定使用過struct
。
但是,你可能不知道的是,通過簡單地重新排序結構中的欄位,可以極大地提高 Go 程式的速度和記憶體使用率!
示例演示
type EmployeeStruct struct {
IsPublic bool
Age int64
Status bool
Name string
Image float32
}
我們來看一下Employee結構體的記憶體大小:
- IsPublic(boolean) 1 位元組
- Age(int64) 8 位元組
- Status(boolean) 1 位元組
- Name(string) 16 位元組
- Image(float32) 4 位元組
總計: 30 位元組
通過unsafe.Sizeof
檢查一下:
package main
import (
"fmt"
"unsafe"
)
type EmployeeStruct struct {
IsPublic bool
Age int64
Status bool
Name string
Image float32
}
func main() {
var employee EmployeeStruct
fmt.Println(unsafe.Sizeof(employee))
}
執行後輸出: 48位元組,為什麼呢?
處理器類型
我們知道CPU 分為32位、34位,但是,它是如何運作的呢?
想象一下我們有一個 64 位的 CPU,每個時鐘周期傳輸 64 位數據的能力。
時鐘周期是CPU處理一個信息需要多少時間,CPU 32位在1個周期內轉換
4位元組數據
,CPU 64位在1個周期內轉換8位元組數據(32位= 4位元組,64位= 8位元組)
上面我們定義的EmployeeStruct
結構體,並且計算得到了每個欄位在記憶體中占用的位元組數。下麵我們看CPU處理信息需要多少時間呢?
先來看下麵的一張圖:
圖裡面6個周期,每個周期有八個盒子,表示是CPU處理能力,為 8 位元組
-
周期1
第一個屬性是IsPublic
,數據類型為布爾型
,bool
的大小為1 個位元組
。因此,當前周期 1 由屬性IsPublic(bool ~ 1 byte)填充。
下一個屬性是Age
,數據類型為int64
,int64
的大小是8位元組
,由於第1個周期的剩餘記憶體大小隻有7個位元組,因此第1個周期無法填充Age
屬性,因此,會進入第2個周期,但是,“剩餘的空間呢,怎麼辦?”。 -
周期2
第2個周期由Age
屬性填充,數據類型為int64
,大小為8位元組
,剛好填充滿。 -
周期3
第三個屬性Status
,數據類型為bool
,大小為1位元組
,填充(bool~1 byte),剩餘記憶體大小為7位元組
,然後,下一個屬性是Name
,類型為字元串
,大小為16 位元組
,與周期1的情況相同,因為沒有足夠的空間容納下一個屬性,所以將在下一個周期中進入。所以意味著有7個位元組的記憶體被浪費了
。 -
周期 4 和周期 5
第四個是Name
,數據類型為字元串
,大小為16位元組
。從圖上面看,它將在兩個周期中填充,周期 4 中填充 8 個位元組,周期 5 中填充 8 個位元組。
-
周期6
最後一個屬性是Image
, 數據類型為float32
,大小為4位元組
,浪費剩餘的4 個位元組
。
最後通過計算統計:
總時鐘周期 = 6 個時鐘周期
結構體大小 = 48 位元組
浪費的總記憶體 = 18 位元組
所以我們在開發的時候,如果對於一個比較大的數據結構體來說,可能會使結構體的大小變得更大。
那麼,如何解決呢?
實際上我們可以根據數據類型的大小來組成序列,最簡單的方法是:
按元素記憶體大小的降序排列欄位
type EmployeeStruct struct {
Name string
Age int64
Image float32
IsPublic bool
Status bool
}
這個時候我們再來看一下時間周期:
-
周期 1 和周期 2
第一個屬性是Name
,數據類型為字元串,大小為16位元組。因此,它將在兩個周期中填充,周期 1 中填充 8 個位元組,周期 2 中填充 8 個位元組。 -
周期3
第3個周期由Age
屬性填充,數據類型為int64
,大小為8位元組 -
周期4
下一個屬性是Image
,數據類型為float32
,大小為4位元組。因此,當前周期 4 由屬性Image ( float32 ~ 4 位元組)
填充,剩餘大小為 4 位元組。
下一個屬性是IsPublic
(bool ~ 1 位元組)和 Status(bool ~ 1 位元組)。因為最後兩個屬性的總大小隻有2個位元組
,所以我們可以將其放在迴圈4中。這樣一來,迴圈4就被Image ( float32 ~ 4位元組)、IsPublic (bool ~ 1位元組)、Status(bool~1 位元組)填滿了,僅浪費 2 位元組記憶體。
最後通過計算統計:
總時鐘周期 = 4 個時鐘周期
結構體大小 = 32 位元組
浪費的總記憶體 = 2 位元組
package main
import (
"fmt"
"unsafe"
)
type EmployeeStruct struct {
Name string
Age int64
Image float32
IsPublic bool
Status bool
}
func main() {
var employee EmployeeStruct
fmt.Println(unsafe.Sizeof(employee)) // output: 32
}
結束
所以我們在開發的時候,重新排序結構欄位是可以提高應用程式的記憶體使用率和運行速度的。
轉載:風向閱讀 - Golang 開發技巧 - 簡單的數據對齊可提高程式速度和記憶體使用率
地址:https://www.aiweimeng.top/archives/56.html