日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

前言

之前遇到過這樣一個(gè)情況(發(fā)現(xiàn)問題的結(jié)構(gòu)體并不長這樣, 不過為了引出問題, 改了一下):

type Test struct {	
  b bool
  i3 int32
  i8 int8
  i64 int64
  by byte
}

func main() {
  t := Test{}
  fmt.Printf("%d", unsafe.Sizeof(t))
}

創(chuàng)建一個(gè)結(jié)構(gòu)體, 查看一下其內(nèi)存占用. 看結(jié)果前先簡單算一下:

  • bool: 1B
  • int32: 4B
  • int8: 1B
  • int64: 8B
  • byte: 1B

這么算下來的話, Test結(jié)構(gòu)體占用應(yīng)該是: 1+4+1+8+1=15B. 15個(gè)字節(jié)對(duì)吧. 來, 打印看一下:

GO 內(nèi)存對(duì)齊

 

32個(gè)字節(jié)???這不坑我么.內(nèi)存占用直接多出一倍.

探索

通過查找資料, 發(fā)現(xiàn)了這樣一個(gè)名詞: 內(nèi)存對(duì)齊. 什么是內(nèi)存對(duì)齊呢?

簡單說, 就是CPU在讀取數(shù)據(jù)的時(shí)候, 并不是一個(gè)字節(jié)一個(gè)字節(jié)讀取的, 而是一塊一塊讀取的. 那么這個(gè)快是多大呢? 根據(jù)CPU位數(shù)不同而不同.

而GO編譯器在編譯的時(shí)候, 為了保證內(nèi)存對(duì)齊, 對(duì)每一個(gè)數(shù)據(jù)類型都給出了對(duì)齊保證, 將未對(duì)齊的內(nèi)存留空. 如果一個(gè)類型的對(duì)齊保證是4B, 那么其數(shù)據(jù)存放的起始地址偏移量必是4B 的整數(shù)倍. 而編譯器給出的這個(gè)對(duì)齊保證是多少呢? 不同版本不同平臺(tái)的編譯器不盡相同, 可以通過函數(shù)unsafe.Alignof 來獲取.

通過分析之前的數(shù)據(jù)結(jié)果, 就能大致理解了. 先來看一下幾個(gè)類型對(duì)齊保證的值:

fmt.Printf("bool: %dn", unsafe.Alignof(bool(false)))	
fmt.Printf("int32: %dn", unsafe.Alignof(int32(0)))	
fmt.Printf("int8: %dn", unsafe.Alignof(int8(0)))	
fmt.Printf("int64: %dn", unsafe.Alignof(int64(0)))
fmt.Printf("byte: %dn", unsafe.Alignof(byte(0)))

結(jié)果如下:

GO 內(nèi)存對(duì)齊

 

來嘗試一個(gè)一個(gè)放到內(nèi)存中(下圖中每個(gè)空白代表一個(gè)字節(jié)):

1.放入bool: 其對(duì)齊保證為1, 第一個(gè)變量, 直接放入即可.

GO 內(nèi)存對(duì)齊

 

2.放入int32. 其對(duì)齊保證為4, 既偏移量為4的整數(shù)倍. 而現(xiàn)有地址中, 首個(gè)4的整數(shù)倍為第四個(gè)字節(jié)(中間三字節(jié)留空).

GO 內(nèi)存對(duì)齊

 

按照這個(gè)思路, 依次將后面的變量放入, 結(jié)果占用的內(nèi)存為(其中字母依次為變量占用, X為對(duì)齊留空):

AXXX BBBB CXXX XXXX DDDD DDDD E

但是這才25個(gè)字節(jié)啊. 和實(shí)際的32字節(jié)還差點(diǎn)呢. 別急, 再看一下結(jié)構(gòu)體的對(duì)齊保證, 發(fā)現(xiàn)是8B. 上面不是8B 的整數(shù)倍, 往后補(bǔ)零. 結(jié)果:

AXXX BBBB CXXX XXXX DDDD DDDD EXXX XXXX

如此一來, 就正好32位了. 結(jié)構(gòu)體的對(duì)齊保證, 為其成員變量對(duì)齊保證的最大值.

why

那么編譯器為什么要做內(nèi)存對(duì)齊這種事情呢? 舉個(gè)例子, 如果不做內(nèi)存對(duì)齊, 那么下面這個(gè)結(jié)構(gòu)體的內(nèi)存分布為:

type Test struct {	
  b   bool
  i3  int32
}

ABBB B

還記得之前說, CPU讀取內(nèi)存是一塊一塊讀取的么? 而這個(gè)塊, 假設(shè)是4B.

這樣的話, 當(dāng)你需要讀取i3變量的時(shí)候, 需要進(jìn)行兩次內(nèi)存訪問. 而對(duì)齊之后, 只需要進(jìn)行一次內(nèi)存訪問即可. 是典型的空間換時(shí)間的做法.

修改

既然知道了問題出在哪里, 那么是不是如果換一下字段的存放順序, 就可以壓縮內(nèi)存空間了呢? 思路很簡單, 將對(duì)齊保證小的放到前面, 試一下:

type Test struct {	
  b   bool
  by  byte
  i8  int8
  i3  int32
  i64 int64
}
func main() {
  t := Test{}	
  fmt.Printf("%d", unsafe.Sizeof(t))
}
GO 內(nèi)存對(duì)齊

 

通過之前的對(duì)齊分析. 結(jié)果確為18B. 也就是因?yàn)樽侄雾樞虻膯栴}, 編譯器為了保證內(nèi)存對(duì)齊, 向其中填充了很多空白, 造成了內(nèi)存的浪費(fèi).

僅僅是修改了一下字段的順序, 就可以將結(jié)構(gòu)體的內(nèi)存占用直接降低一倍. 見識(shí)了...

檢測(cè)工具

那么, 有沒有什么辦法能夠幫我們檢測(cè)是否存在內(nèi)存對(duì)齊的優(yōu)化呢? 畢竟平常寫的時(shí)候, 誰會(huì)關(guān)心這玩意呢. 別說, 還真有. golangci-lint

官網(wǎng): https://golangci-lint.run/

安裝: brew install golangci-lint

檢測(cè)所有文件命令: golangci-lint run ./..

檢測(cè)一下最開始的結(jié)構(gòu)體文件(添加參數(shù)指定檢測(cè)內(nèi)存對(duì)齊):

golangci-lint run --disable-all -E maligned main.go

看到結(jié)果:

GO 內(nèi)存對(duì)齊

 

會(huì)看到提示, 該結(jié)構(gòu)體當(dāng)前占有32B, 可優(yōu)化至16B. 完美.

當(dāng)然, 此工具的功能不僅如此, 它能夠提供很多建議, 有待發(fā)掘.


其實(shí), 項(xiàng)目中估計(jì)也很少有關(guān)注內(nèi)存對(duì)齊的時(shí)候吧. 不過畢竟積少成多, 內(nèi)存這玩意, 能省則省嘛.

分享到:
標(biāo)簽:對(duì)齊 內(nèi)存
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績?cè)u(píng)定