您好,登錄后才能下訂單哦!
這篇文章主要介紹“golang內存對齊的概念是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“golang內存對齊的概念是什么”文章能幫助大家解決問題。
為保證程序順利高效的運行,編譯器會把各種類型的數據安排到合適的地址,并占用合適的長度,這就是內存對齊。
每種類型的對齊值就是它的對齊邊界,內存對齊要求數據存儲地址以及占用的字節數都要是它的對齊邊界的倍數。所以下述的int32要錯開兩個字節,從4開始存,卻不能緊接著從2開始。
也可以這樣解釋:
CPU把內存當成是一塊一塊的,塊的大小可以是2,4,8,16字節大小,因此CPU在讀取內存時是一塊一塊進行讀取的。塊大小成為memory access granularity(粒度)。
如果不進行內存對齊
比如我們想從地址1開始讀8字節的數據:
CPU會分兩次讀:
第一次從 0 - 7
但只取后 7
字節。
第二次從 8 - 15
但只取第 1
字節。
分兩次讀,這樣勢必會對性能造成影響。
原因主要有兩點:
平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。
性能原因:數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。
那該怎么確定每種數據的對齊邊界呢?這和平臺有關,go語言支持這些平臺:
可以看到常見的32位平臺,指針寬度和寄存器寬度都是4字節,64位平臺上都是8字節。而被go語言稱為寄存器寬度的這個值,就可以理解為機器字長,也是平臺對應的最大對齊邊界。
而數據類型的對齊邊界,是取類型大小與平臺最大對齊邊界中較小的那個。不過要注意,同一個類型在不同平臺上,大小可能不同,對齊邊界也可能不同。
為什么不統一使用平臺最大對齊邊界呢?或者統一按各類型大小來對齊呢?
我們來試一下,假設目前是64位平臺,最大對齊邊界為8字節。int8只有1字節,按照1字節對齊的話,它可以放在任何位置,因為總能通過一次讀取把它完整拿出來。如果統一對齊到8字節,雖然同樣只要讀取一次,但每個int8的變量都要浪費7字節,所以對齊到1。
int16占2字節,按照2字節對齊,可以從這些地址開始存,而且能保證只用讀取一次。
如果按1字節對齊就可能存成這樣,那就要讀取兩次再截取拼接,會影響性能。
如果按8字節對齊,會與int8一樣浪費內存,所以對齊到2。
這是小于最大對齊邊界的情況,再來看看大于的情況。
假設要在32位的平臺下存儲一個int64類型的數據,在0和1位置被占用的情況下,就要從位置8開始存。而如果對齊到4,就可以從位置4開始,內存浪費更少,所以選擇對齊到4。
因此類型對齊邊界會這樣選擇,依然是為了減少浪費提升性能。
在go語言中可以調用 unsafe.Alignof
來返回相應類型的對齊邊界:
func main() { fmt.Printf("bool align: %d\n", unsafe.Alignof(bool(true))) fmt.Printf("int32 align: %d\n", unsafe.Alignof(int32(0))) fmt.Printf("int8 align: %d\n", unsafe.Alignof(int8(0))) fmt.Printf("int64 align: %d\n", unsafe.Alignof(int64(0))) fmt.Printf("byte align: %d\n", unsafe.Alignof(byte(0))) fmt.Printf("string align: %d\n", unsafe.Alignof("EDDYCJY")) fmt.Printf("map align: %d\n", unsafe.Alignof(map[string]string{})) }
運行結果:
bool align: 1
int32 align: 4
int8 align: 1
int64 align: 8
byte align: 1
string align: 8
map align: 8
對結構體而言,首先要確定每個成員的對齊邊界,然后取其中最大的,這就是這個結構體的對齊邊界。
然后來存儲這個結構體變量:
內存對齊要求一:
存儲這個結構體的起始地址,是對齊邊界的倍數。
假設從0開始存,結構體的每個成員在存儲時,都要把這個起始地址當作地址0,然后再用相對地址來決定自己該放在哪里。
內存對齊要求2:
結構體整體占用字節數需要是類型對齊邊界的倍數,不夠的話要往后擴張一下。
所以最終上述結構體類型的大小就是24字節。
案例
type Part1 struct { a bool b int32 c int8 d int64 e byte }
type Part2 struct { a bool c int8 e byte b int32 // 4個字節 d int64 }
分別求以上兩個結構體占用的字節:
fmt.Printf("part1 size: %d, align: %d\n", unsafe.Sizeof(part1), unsafe.Alignof(part1)) fmt.Printf("part2 size: %d, align: %d\n", unsafe.Sizeof(part2), unsafe.Alignof(part2))
這里我們直接調用函數求得:
part1 size: 32, align: 8 part2 size: 16, align: 8
關于“golang內存對齊的概念是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。