最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go內(nèi)存節(jié)省技巧簡單實(shí)現(xiàn)方法

 更新時間:2023年01月06日 14:13:07   作者:lsj1342?GoCN  
這篇文章主要為大家介紹了Go內(nèi)存節(jié)省技巧簡單實(shí)現(xiàn)方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

除非您正在對服務(wù)進(jìn)行原型設(shè)計,否則您可能會關(guān)心應(yīng)用程序的內(nèi)存使用情況。占用更小的內(nèi)存,會使基礎(chǔ)設(shè)施成本降低,擴(kuò)展變得更容易。盡管 Go 以不消耗大量內(nèi)存而聞名,但仍有一些方法可以進(jìn)一步減少消耗。其中一些需要大量重構(gòu),但很多都很容易做到。

預(yù)先分配切片

要理解這種優(yōu)化,我們必須了解切片在 Go 中是如何工作的,為此我們必須首先了解數(shù)組。

go.dev 上有一篇非常好的關(guān)于這個主題的文章。

數(shù)組是具有連續(xù)內(nèi)存的相同類型的集合。數(shù)組類型定義時要指定長度和元素類型。

因?yàn)閿?shù)組的長度是它們類型的一部分,數(shù)組的主要問題是它們大小固定,不能調(diào)整。

與數(shù)組類型不同,切片類型無需指定長度。切片的聲明方式與數(shù)組相同,但沒有數(shù)量元素。

切片是數(shù)組的包裝器,它們不擁有任何數(shù)據(jù)——它們是對數(shù)組的引用。它們由指向數(shù)組的指針、長度及其容量(底層數(shù)組中的元素數(shù))組成。

當(dāng)您向沒有足夠容量的切片添加一個新值時 - 會創(chuàng)建一個具有更大容量的新數(shù)組,并將當(dāng)前數(shù)組中的值復(fù)制到新數(shù)組中。這會導(dǎo)致不必要的內(nèi)存分配和 CPU 周期。

為了更好地理解這一點(diǎn),讓我們看一下以下代碼段:

func?main()?{
????var?ints?[]int
????for?i?:=?0;?i?<?5;?i++?{
????????ints?=?append(ints,?i)
????????fmt.Printf("Address:?%p,?Length:?%d,?Capacity:?%d,?Values:?%v\n",
????????????ints,?len(ints),?cap(ints),?ints)
????}
}

輸出:

Address: 0xc000018030, Length: 1, Capacity: 1, Values: [0]
Address: 0xc000018050, Length: 2, Capacity: 2, Values: [0 1]
Address: 0xc000082020, Length: 3, Capacity: 4, Values: [0 1 2]
Address: 0xc000082020, Length: 4, Capacity: 4, Values: [0 1 2 3]
Address: 0xc000084040, Length: 5, Capacity: 8, Values: [0 1 2 3 4]

憑借輸出結(jié)果我們可以得出結(jié)論,無論何時必須增加容量(增加 2 倍),都必須創(chuàng)建一個新的底層數(shù)組(新的內(nèi)存地址)并將值復(fù)制到新數(shù)組中。

有趣是,當(dāng)容量<1024 時會漲為之前的 2 倍,當(dāng)容量>=1024時會以 1.25 倍增長。從 Go 1.18 開始,這已經(jīng)變得更加線性。

Address:?0xc000018030,?Length:?1,?Capacity:?1,?Values:?[0]
Address:?0xc000018050,?Length:?2,?Capacity:?2,?Values:?[0?1]
Address:?0xc000082020,?Length:?3,?Capacity:?4,?Values:?[0?1?2]
Address:?0xc000082020,?Length:?4,?Capacity:?4,?Values:?[0?1?2?3]
Address:?0xc000084040,?Length:?5,?Capacity:?8,?Values:?[0?1?2?3?4]func?BenchmarkAppend(b?*testing.B)?{
????var?ints?[]int
????for?i?:=?0;?i?<?b.N;?i++?{
????????ints?=?append(ints,?i)
????}
}
func?BenchmarkPreallocAssign(b?*testing.B)?{
????ints?:=?make([]int,?b.N)
????for?i?:=?0;?i?<?b.N;?i++?{
????????ints[i]?=?i
????}
}
func?BenchmarkAppend(b?*testing.B)?{
????var?ints?[]int
????for?i?:=?0;?i?<?b.N;?i++?{
????????ints?=?append(ints,?i)
????}
}
func?BenchmarkPreallocAssign(b?*testing.B)?{
????ints?:=?make([]int,?b.N)
????for?i?:=?0;?i?<?b.N;?i++?{
????????ints[i]?=?i
????}
}

name               time/op
Append-10          3.81ns ± 0%
PreallocAssign-10  0.41ns ± 0%

name               alloc/op
Append-10           45.0B ± 0%
PreallocAssign-10   8.00B ± 0%

name               allocs/op
Append-10            0.00
PreallocAssign-10    0.00

由上述基準(zhǔn),我們可以得出結(jié)論,將值分配給預(yù)分配的切片和將值追加到切片之間是存在很大差異的。

兩個工具有助于切片的預(yù)分配:

  • prealloc: 一個靜態(tài)分析工具,用于查找可能被預(yù)分配的切片聲明。
  • makezero: 一個靜態(tài)分析工具,用于查找未以零長度初始化且稍后有追加的切片聲明。

結(jié)構(gòu)體中的字段順序

您之前可能沒有想到這一點(diǎn),但結(jié)構(gòu)體中字段的順序?qū)?nèi)存消耗有很大影響。

以下面的結(jié)構(gòu)體為例:

type?Post?struct?{
????IsDraft?????bool??????//?1?byte
????Title???????string????//?16?bytes
????ID??????????int64?????//?8?bytes
????Description?string????//?16?bytes
????IsDeleted???bool??????//?1?byte
????Author??????string????//?16?bytes
????CreatedAt???time.Time?//?24?bytes
}
func?main(){
????p?:=?Post{}
????fmt.Println(unsafe.Sizeof(p))
}

上述的輸出為 96 字節(jié),而所有字段相加為 82 字節(jié)。那額外的 14 個字節(jié)是來自哪里呢?

現(xiàn)代 64 位 CPU 以 64 位(8 字節(jié))的塊獲取數(shù)據(jù)。如果我們有一個較舊的 32 位 CPU,它將以 32 位(4 字節(jié))的塊進(jìn)行。

第一個周期占用 8 個字節(jié),拉取“IsDraft”字段占用了 1 個字節(jié)并且產(chǎn)生 7 個未使用字節(jié)。它不能占用“一半”的字段。

第二個和第三個周期取 Title 字符串,第四個周期取 ID,依此類推。到取 IsDeleted 字段時,它使用 1 個字節(jié)并有 7 個字節(jié)未使用。

對內(nèi)存節(jié)省的關(guān)鍵是按字段占用大小從上到下對字段進(jìn)行排序。對上述結(jié)構(gòu)進(jìn)行排序,大小可減少到 88 個字節(jié)。最后兩個字段 IsDraft 和 IsDeleted 被放在同一個塊中,從而將未使用的字節(jié)數(shù)從 14 (2x7) 減少到 6 (1 x 6),在此過程中節(jié)省了 8 個字節(jié)。

type?Post?struct?{
????CreatedAt???time.Time?//?24?bytes
????Title???????string????//?16?bytes
????Description?string????//?16?bytes
????Author??????string????//?16?bytes
????ID??????????int64?????//?8?bytes
????IsDraft?????bool??????//?1?byte
????IsDeleted???bool??????//?1?byte
}
func?main(){
????p?:=?Post{}
????fmt.Println(unsafe.Sizeof(p))
}

在 64 位架構(gòu)上占用小于 8 字節(jié)的 Go 類型:

  • bool: 1 個字節(jié)
  • int8/uint8: 1 個字節(jié)
  • int16/uint16: 2 個字節(jié)
  • int32/uint32/rune: 4 個字節(jié)
  • float32: 4 個字節(jié)
  • byte: 1 個字節(jié)

不需要手動檢查您的結(jié)構(gòu)體并按大小對其進(jìn)行排序,而是使用 工具找到這些結(jié)構(gòu)并報告“正確”的排序。

  • maligned - 已棄用,用于報告未對齊的結(jié)構(gòu)并打印出正確排序的字段。它在一年前被棄用,但您仍然可以安裝舊版本并使用它。
  • govet/fieldalignment: gotools 和 govet 的一部分工具,fieldalignment 可打印出未對齊的結(jié)構(gòu)和結(jié)構(gòu)的當(dāng)前/理想大小。

安裝和運(yùn)行 fieldalignment:

go?install?golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest
fieldalignment?-fix?&lt;package_path&gt;

對上面的代碼使用 govet/fieldalignment:

fieldalignment:?struct?of?size?96?could?be?88?(govet)

使用 map[string]struct{} 而不是 map[string]bool

Go 沒有內(nèi)置的集合,通常使用 map[string]bool{} 表示集合。盡管它更具可讀性(這非常重要),但將其作為一個集合使用是錯誤的,因?yàn)樗哂袃煞N狀態(tài)(假/真)并且與空結(jié)構(gòu)體相比使用了額外的內(nèi)存。

空結(jié)構(gòu)體 (struct{}) 是沒有額外字段的結(jié)構(gòu)類型,占用零字節(jié)的存儲空間。Dave Chaney 有一篇關(guān)于空結(jié)構(gòu)的詳細(xì)博客 。

除非您的 map/set 包含大量值并且需要獲得額外的內(nèi)存,否則我建議使用 map[string]struct{}。

100 000 000 次 map 寫入的極端示例:

func?BenchmarkBool(b?*testing.B)?{
????m?:=?make(map[uint]bool)
????for?i?:=?uint(0);?i?<?100_000_000;?i++?{
????????m[i]?=?true
????}
}
func?BenchmarkEmptyStruct(b?*testing.B)?{
????m?:=?make(map[uint]struct{})
????for?i?:=?uint(0);?i?<?100_000_000;?i++?{
????????m[i]?=?struct{}{}
????}
}

多次運(yùn)行程序得到的結(jié)果一致(MBP 14 2021,10C M1 Pro):

name            time/op
Bool          12.4s ± 0%
EmptyStruct   12.0s ± 0%

name            alloc/op
Bool         3.78GB ± 0%
EmptyStruct  3.43GB ± 0%

name            allocs/op
Bool          3.91M ± 0%
EmptyStruct   3.90M ± 0%

通過這些數(shù)字,我們可以得出結(jié)論,使用空結(jié)構(gòu)映射的寫入速度提高了 3.2%,分配的內(nèi)存減少了 10%。

此外,使用map[type]struct{}是實(shí)現(xiàn)集合的正確解決方法,因?yàn)槊總€鍵都有一個值。map[type]bool 每個鍵有兩個可能的值,這不是一個集合,如果目標(biāo)是創(chuàng)建一個集合,則可能會被濫用。

然而,可讀性大多數(shù)時候比(可忽略的)內(nèi)存改進(jìn)更重要。與空結(jié)構(gòu)體相比,使用布爾值更容易查找:

m?:=?make(map[string]bool{})
if?m["key"]{
?//?Do?something
}
v?:=?make(map[string]struct{}{})
if?_,?ok?:=?v["key"];?ok{
????//?Do?something
}

以上就是Go內(nèi)存節(jié)省技巧簡單實(shí)現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于Go 內(nèi)存節(jié)省的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語言指針使用分析與講解

    Go語言指針使用分析與講解

    這篇文章主要介紹了Go語言指針使用分析與講解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • go?module化?import?調(diào)用本地模塊?tidy的方法

    go?module化?import?調(diào)用本地模塊?tidy的方法

    這篇文章主要介紹了go?module化?import?調(diào)用本地模塊?tidy的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-09-09
  • 小學(xué)生也能看懂的Golang異常處理recover panic

    小學(xué)生也能看懂的Golang異常處理recover panic

    在其他語言里,宕機(jī)往往以異常的形式存在,底層拋出異常,上層邏輯通過 try/catch 機(jī)制捕獲異常,沒有被捕獲的嚴(yán)重異常會導(dǎo)致宕機(jī),go語言追求簡潔,優(yōu)雅,Go語言不支持傳統(tǒng)的 try…catch…finally 這種異常
    2021-09-09
  • golang多次讀取http request body的問題分析

    golang多次讀取http request body的問題分析

    這篇文章主要給大家分析了golang多次讀取http request body的問題,文中通過代碼示例和圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-01-01
  • Go語言如何并發(fā)超時處理詳解

    Go語言如何并發(fā)超時處理詳解

    大家都知道golang并沒有在語言層次上提供超時操作,但可以通過一些小技巧實(shí)現(xiàn)超時。下面來一起看看吧,有需要的朋友們可以參考借鑒。
    2016-09-09
  • Golang中字符串(string)與字節(jié)數(shù)組([]byte)一行代碼互轉(zhuǎn)實(shí)例

    Golang中字符串(string)與字節(jié)數(shù)組([]byte)一行代碼互轉(zhuǎn)實(shí)例

    golang語言本身就是c的工具集,開發(fā)c的程序用到的大部分結(jié)構(gòu)體,內(nèi)存管理,攜程等,golang基本都有,下面這篇文章主要給大家介紹了關(guān)于Golang中字符串(string)與字節(jié)數(shù)組([]byte)一行代碼互轉(zhuǎn)的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • Go空結(jié)構(gòu)體struct{}的作用是什么

    Go空結(jié)構(gòu)體struct{}的作用是什么

    本文主要介紹了Go空結(jié)構(gòu)體struct{}的作用是什么,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • 基于Golang 高并發(fā)問題的解決方案

    基于Golang 高并發(fā)問題的解決方案

    這篇文章主要介紹了Golang 高并發(fā)問題的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • 在Golang中讀寫CSV文件的操作指南

    在Golang中讀寫CSV文件的操作指南

    CSV(逗號分隔值)文件是一種常見的數(shù)據(jù)存儲格式,廣泛應(yīng)用于數(shù)據(jù)導(dǎo)入、導(dǎo)出、分析和交換等場景,在Golang中,有許多庫和工具可以幫助我們讀取和寫入CSV文件,使數(shù)據(jù)處理變得簡單而高效,本文將深入探討如何在Golang中使用標(biāo)準(zhǔn)庫以及第三方庫來讀寫CSV文件
    2023-11-11
  • 淺析Go中函數(shù)的健壯性,panic異常處理和defer機(jī)制

    淺析Go中函數(shù)的健壯性,panic異常處理和defer機(jī)制

    這篇文章主要為大家詳細(xì)介紹了Go中函數(shù)的健壯性,panic異常處理和defer機(jī)制的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2023-10-10

最新評論

红原县| 乐山市| 普宁市| 平山县| 古田县| 德保县| 蓬莱市| 佛冈县| 阿巴嘎旗| 荥阳市| 方正县| 望奎县| 定远县| 新平| 东安县| 深泽县| 荆门市| 贡觉县| 金门县| 元阳县| 凌源市| 淮安市| 惠州市| 子洲县| 隆尧县| 磴口县| 银川市| 开阳县| 保康县| 婺源县| 如皋市| 遂平县| 安图县| 山西省| 阿克陶县| 沙湾县| 温泉县| 玉树县| 大化| 黄大仙区| 平谷区|