欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang內(nèi)存對(duì)齊的概念及案例詳解

 更新時(shí)間:2022年02月02日 09:10:52   作者:Dawnlight-_-  
為保證程序順利高效的運(yùn)行,編譯器會(huì)把各種類型的數(shù)據(jù)安排到合適的地址,并占用合適的長度,這就是內(nèi)存對(duì)齊。本文重點(diǎn)給大家介紹golang內(nèi)存對(duì)齊的概念及案例詳解,感興趣的朋友一起看看吧

什么是內(nèi)存對(duì)齊

為保證程序順利高效的運(yùn)行,編譯器會(huì)把各種類型的數(shù)據(jù)安排到合適的地址,并占用合適的長度,這就是內(nèi)存對(duì)齊。

每種類型的對(duì)齊值就是它的對(duì)齊邊界,內(nèi)存對(duì)齊要求數(shù)據(jù)存儲(chǔ)地址以及占用的字節(jié)數(shù)都要是它的對(duì)齊邊界的倍數(shù)。所以下述的int32要錯(cuò)開兩個(gè)字節(jié),從4開始存,卻不能緊接著從2開始。

也可以這樣解釋:

CPU把內(nèi)存當(dāng)成是一塊一塊的,塊的大小可以是2,4,8,16字節(jié)大小,因此CPU在讀取內(nèi)存時(shí)是一塊一塊進(jìn)行讀取的。塊大小成為memory access granularity(粒度)。

如果不進(jìn)行內(nèi)存對(duì)齊

比如我們想從地址1開始讀8字節(jié)的數(shù)據(jù):

CPU會(huì)分兩次讀:

  • 第一次從 0 - 7 但只取后 7 字節(jié)。
  • 第二次從 8 - 15 但只取第 1 字節(jié)。

分兩次讀,這樣勢(shì)必會(huì)對(duì)性能造成影響。

為什么要內(nèi)存對(duì)齊

原因主要有兩點(diǎn):

  • 平臺(tái)原因(移植原因):不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
  • 性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。原因在于,為了訪問未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對(duì)齊的內(nèi)存訪問僅需要一次訪問。

對(duì)齊邊界

那該怎么確定每種數(shù)據(jù)的對(duì)齊邊界呢?這和平臺(tái)有關(guān),go語言支持這些平臺(tái):

可以看到常見的32位平臺(tái),指針寬度和寄存器寬度都是4字節(jié),64位平臺(tái)上都是8字節(jié)。而被go語言稱為寄存器寬度的這個(gè)值,就可以理解為機(jī)器字長,也是平臺(tái)對(duì)應(yīng)的最大對(duì)齊邊界。

而數(shù)據(jù)類型的對(duì)齊邊界,是取類型大小與平臺(tái)最大對(duì)齊邊界中較小的那個(gè)。不過要注意,同一個(gè)類型在不同平臺(tái)上,大小可能不同,對(duì)齊邊界也可能不同。

為什么不統(tǒng)一使用平臺(tái)最大對(duì)齊邊界呢?或者統(tǒng)一按各類型大小來對(duì)齊呢?

我們來試一下,假設(shè)目前是64位平臺(tái),最大對(duì)齊邊界為8字節(jié)。int8只有1字節(jié),按照1字節(jié)對(duì)齊的話,它可以放在任何位置,因?yàn)榭偰芡ㄟ^一次讀取把它完整拿出來。如果統(tǒng)一對(duì)齊到8字節(jié),雖然同樣只要讀取一次,但每個(gè)int8的變量都要浪費(fèi)7字節(jié),所以對(duì)齊到1。

int16占2字節(jié),按照2字節(jié)對(duì)齊,可以從這些地址開始存,而且能保證只用讀取一次。

如果按1字節(jié)對(duì)齊就可能存成這樣,那就要讀取兩次再截取拼接,會(huì)影響性能。

如果按8字節(jié)對(duì)齊,會(huì)與int8一樣浪費(fèi)內(nèi)存,所以對(duì)齊到2。

這是小于最大對(duì)齊邊界的情況,再來看看大于的情況。

假設(shè)要在32位的平臺(tái)下存儲(chǔ)一個(gè)int64類型的數(shù)據(jù),在0和1位置被占用的情況下,就要從位置8開始存。而如果對(duì)齊到4,就可以從位置4開始,內(nèi)存浪費(fèi)更少,所以選擇對(duì)齊到4。

因此類型對(duì)齊邊界會(huì)這樣選擇,依然是為了減少浪費(fèi)提升性能。

GO 計(jì)算對(duì)齊邊界函數(shù)

在go語言中可以調(diào)用 unsafe.Alignof 來返回相應(yīng)類型的對(duì)齊邊界:

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{}))
}

運(yùn)行結(jié)果:

bool align: 1
int32 align: 4
int8 align: 1
int64 align: 8
byte align: 1
string align: 8
map align: 8

確定結(jié)構(gòu)體的對(duì)齊邊界

對(duì)結(jié)構(gòu)體而言,首先要確定每個(gè)成員的對(duì)齊邊界,然后取其中最大的,這就是這個(gè)結(jié)構(gòu)體的對(duì)齊邊界。

然后來存儲(chǔ)這個(gè)結(jié)構(gòu)體變量:

內(nèi)存對(duì)齊要求一:

  • 存儲(chǔ)這個(gè)結(jié)構(gòu)體的起始地址,是對(duì)齊邊界的倍數(shù)。

?假設(shè)從0開始存,結(jié)構(gòu)體的每個(gè)成員在存儲(chǔ)時(shí),都要把這個(gè)起始地址當(dāng)作地址0,然后再用相對(duì)地址來決定自己該放在哪里。

內(nèi)存對(duì)齊要求2:

  • 結(jié)構(gòu)體整體占用字節(jié)數(shù)需要是類型對(duì)齊邊界的倍數(shù),不夠的話要往后擴(kuò)張一下。

?所以最終上述結(jié)構(gòu)體類型的大小就是24字節(jié)。

案例

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個(gè)字節(jié)
	d int64
}

分別求以上兩個(gè)結(jié)構(gòu)體占用的字節(jié):

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))

這里我們直接調(diào)用函數(shù)求得:

part1 size: 32, align: 8
part2 size: 16, align: 8

原因請(qǐng)讀者來思考。

參考資料:
https://blog.csdn.net/u010853261/article/details/102557188
https://www.bilibili.com/video/BV1Ja4y1i7AF?from=search&seid=16213689667007976568&spm_id_from=333.337.0.0

到此這篇關(guān)于golang內(nèi)存對(duì)齊的文章就介紹到這了,更多相關(guān)golang內(nèi)存對(duì)齊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 從零封裝Gin框架實(shí)現(xiàn)數(shù)據(jù)庫初始化GORM

    從零封裝Gin框架實(shí)現(xiàn)數(shù)據(jù)庫初始化GORM

    這篇文章主要為大家介紹了從零封裝Gin框架實(shí)現(xiàn)數(shù)據(jù)庫初始化GORM,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • go語法入門泛型type?parameters簡稱T(類型形參)兩種場(chǎng)景使用

    go語法入門泛型type?parameters簡稱T(類型形參)兩種場(chǎng)景使用

    這篇文章主要為大家介紹了go語法入門泛型type?parameters簡稱T(類型形參)兩種場(chǎng)景使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Go1.21新增maps包的用法詳解

    Go1.21新增maps包的用法詳解

    maps?包提供了幾個(gè)非常有用的用于操作?map?類型(任何類型的?map)的函數(shù),本文為大家整理了部分函數(shù)的具體用法,感興趣的小伙伴可以了解一下
    2023-08-08
  • 淺析Go語言中數(shù)組的這些細(xì)節(jié)

    淺析Go語言中數(shù)組的這些細(xì)節(jié)

    這篇文章主要為大家詳細(xì)介紹了Go語言中數(shù)組一些細(xì)節(jié)的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語言有一定的幫助,需要的可以了解一下
    2022-11-11
  • 一文帶你徹底搞懂 Golang 中的方法(Methods)

    一文帶你徹底搞懂 Golang 中的方法(Methods)

    Golang 支持一些類似面向?qū)ο缶幊痰奶匦?,方法就其中之一,本文將詳?xì)介紹 Golang 中方法相關(guān)的知識(shí),感興趣的小伙伴跟著小編一起來學(xué)習(xí)吧
    2023-07-07
  • 十個(gè)Golang開發(fā)中應(yīng)該避免的錯(cuò)誤總結(jié)

    十個(gè)Golang開發(fā)中應(yīng)該避免的錯(cuò)誤總結(jié)

    Go是一種靜態(tài)類型的、并發(fā)的、垃圾收集的編程語言,由谷歌開發(fā)。開發(fā)人員在編寫Go代碼時(shí)總會(huì)有一些常見的錯(cuò)誤,下面是Go語言中需要避免的十大壞錯(cuò)誤,希望對(duì)大家有所幫助
    2023-03-03
  • Go語言的io輸入輸出流方式

    Go語言的io輸入輸出流方式

    Go語言中,輸入輸出流的處理通過io庫中的Reader和Writer接口來實(shí)現(xiàn),Reader接口定義了Read方法,用于從流中讀取數(shù)據(jù)到程序中,Writer接口定義了Write方法,用于將數(shù)據(jù)寫入到底層的數(shù)據(jù)流中,這些接口被許多標(biāo)準(zhǔn)庫的類型所實(shí)現(xiàn)
    2024-10-10
  • go語言實(shí)現(xiàn)屏幕截圖的示例代碼

    go語言實(shí)現(xiàn)屏幕截圖的示例代碼

    屏幕截圖在很多地方都可以 用到,本文主要介紹了go語言實(shí)現(xiàn)屏幕截圖的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Go+Vue開發(fā)一個(gè)線上外賣應(yīng)用的流程(用戶名密碼和圖形驗(yàn)證碼)

    Go+Vue開發(fā)一個(gè)線上外賣應(yīng)用的流程(用戶名密碼和圖形驗(yàn)證碼)

    這篇文章主要介紹了Go+Vue開發(fā)一個(gè)線上外賣應(yīng)用(用戶名密碼和圖形驗(yàn)證碼),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • golang如何實(shí)現(xiàn)三元運(yùn)算符功能

    golang如何實(shí)現(xiàn)三元運(yùn)算符功能

    這篇文章主要介紹了在其他一些編程語言中,如?C?語言,三元運(yùn)算符是一種可以用一行代碼實(shí)現(xiàn)條件選擇的簡便方法,那么在Go語言中如何實(shí)現(xiàn)類似功能呢,下面就跟隨小編一起學(xué)習(xí)一下吧
    2024-02-02

最新評(píng)論