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

Golang 內(nèi)存管理簡單技巧詳解

 更新時間:2022年08月22日 09:22:23   作者:宇宙之一粟  
這篇文章主要為大家介紹了Golang 內(nèi)存管理簡單技巧詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(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ù)先分配切片

數(shù)組是具有連續(xù)內(nèi)存的相同類型的集合。數(shù)組類型定義指定長度和元素類型。數(shù)組的主要問題是它們的大小是固定的——它們不能調(diào)整大小,因為數(shù)組的長度是它們類型的一部分。

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

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

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

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

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: 0xc0000160c8, Length: 1, Capacity: 1, Values: [0]
Address: 0xc0000160f0, Length: 2, Capacity: 2, Values: [0 1]
Address: 0xc00001e080, Length: 3, Capacity: 4, Values: [0 1 2]
Address: 0xc00001e080, Length: 4, Capacity: 4, Values: [0 1 2 3]
Address: 0xc00001a140, Length: 5, Capacity: 8, Values: [0 1 2 3 4]

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

有趣的事實是,容量增長的因素曾經(jīng)是容量 <1024 的 2 倍,以及 >= 1024 的 1.25 倍。從 Go 1.18 開始,這已經(jīng)變得更加線性。

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ù)分配的切片和將值附加到切片之間存在很大差異。

兩個 linter 有助于預(yù)分配切片:

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

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

您之前可能沒有想到這一點,但結(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))
}

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

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

第一個周期占用 8 個字節(jié),IsDraft 字段占用 1 個字節(jié),并有 7 個未使用字節(jié)。它不能占據(jù)一個字段的“一半”。

第二和第三個循環(huán)取 Title 字符串,第四個循環(huán)取 ID,依此類推。再次使用 IsDeleted 字段,它需要 1 個字節(jié)并有 7 個未使用的字節(jié)。

真正重要的是按字段的大小從上到下對字段進(jìn)行排序。對上述結(jié)構(gòu)進(jìn)行排序,大小減少到 88 個字節(jié)。最后兩個字段 IsDraftIsDeleted 被放在同一個塊中,從而將未使用的字節(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
    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)行排序,而是使用 linter 找到這些結(jié)構(gòu)并(用于)報告“正確”排序。

  • maligned: 不推薦使用的 linter,用于報告未對齊的結(jié)構(gòu)并打印出正確排序的字段。它在一年前被棄用,但您仍然可以安裝舊版本并使用它。
  • govet/fieldalignment: 作為 gotools 和 govet linter 的一部分,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 <package_path>

在上面的代碼中使用 govet/fieldalignment:

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

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

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

空結(jié)構(gòu)體 (struct{}) 是沒有額外字段的結(jié)構(gòu)體類型,占用零字節(jié)存儲空間。

我不建議這樣做,除非您的 map/set 包含大量值并且您需要獲得額外的內(nèi)存或者您正在為低內(nèi)存平臺進(jìn)行開發(fā)。

使用 100 000 000 次寫入地圖的極端示例:

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

得到以下結(jié)果,在整個運(yùn)行過程中非常一致:

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{} 是實現(xià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
}

參考鏈接:Easy memory-saving tricks in Go | Emir Ribic (ribice.ba)

以上就是Golang 內(nèi)存管理簡單技巧詳解的詳細(xì)內(nèi)容,更多關(guān)于Golang 內(nèi)存管理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Go語言單元測試中如何解決MySQL存儲依賴問題

    詳解Go語言單元測試中如何解決MySQL存儲依賴問題

    MySQL?存儲就是一個非常常見的外部依賴,這篇文章主要來和大家一起探討在?Go?語言中編寫單元測試時,如何解決?MySQL?存儲依賴,需要的可以參考一下
    2023-07-07
  • golang 生成二維碼海報的實現(xiàn)代碼

    golang 生成二維碼海報的實現(xiàn)代碼

    這篇文章主要介紹了golang 生成二維碼海報的實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • GoLand一鍵上傳項目到遠(yuǎn)程服務(wù)器的方法步驟

    GoLand一鍵上傳項目到遠(yuǎn)程服務(wù)器的方法步驟

    我們開發(fā)項目常常將項目上傳到linux遠(yuǎn)程服務(wù)器上來運(yùn)行,本文主要介紹了GoLand一鍵上傳項目到遠(yuǎn)程服務(wù)器的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Go語言包和包管理詳解

    Go語言包和包管理詳解

    這篇文章主要為大家介紹了Go語言包和包管理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Golang Defer作用域及執(zhí)行順序使用案例

    Golang Defer作用域及執(zhí)行順序使用案例

    這篇文章主要為大家介紹了Golang Defer作用域及執(zhí)行順序使用案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go語言的文件操作代碼匯總

    Go語言的文件操作代碼匯總

    本文給大家匯總介紹了go語言中的文件操作的代碼,包括文件的讀寫,文件的新建打開和刪除等,希望對大家學(xué)習(xí)go語言能夠有所幫助
    2018-10-10
  • Go語言中三種容器類型的數(shù)據(jù)結(jié)構(gòu)詳解

    Go語言中三種容器類型的數(shù)據(jù)結(jié)構(gòu)詳解

    在?Go?語言中,有三種主要的容器類型用于存儲和操作集合數(shù)據(jù)這篇文章主要為大家介紹了三者的使用與區(qū)別,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-02-02
  • Go依賴注入工具wire的具體使用

    Go依賴注入工具wire的具體使用

    本文主要介紹了Go依賴注入工具wire的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Golang?編寫Tcp服務(wù)器的解決方案

    Golang?編寫Tcp服務(wù)器的解決方案

    Golang?作為廣泛用于服務(wù)端和云計算領(lǐng)域的編程語言,tcp?socket?是其中至關(guān)重要的功能,這篇文章給大家介紹Golang?開發(fā)?Tcp?服務(wù)器及拆包粘包、優(yōu)雅關(guān)閉的解決方案,感興趣的朋友一起看看吧
    2022-10-10
  • golang 監(jiān)聽服務(wù)的信號,實現(xiàn)平滑啟動,linux信號說明詳解

    golang 監(jiān)聽服務(wù)的信號,實現(xiàn)平滑啟動,linux信號說明詳解

    這篇文章主要介紹了golang 監(jiān)聽服務(wù)的信號,實現(xiàn)平滑啟動,linux信號說明詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05

最新評論

上杭县| 历史| 隆林| 榆中县| 湟源县| 肃北| 太白县| 海盐县| 贵南县| 太保市| 黑龙江省| 鄂伦春自治旗| 崇义县| 东莞市| 岳普湖县| 琼海市| 阿坝县| 广德县| 正宁县| 织金县| 蕉岭县| 电白县| 长汀县| 邻水| 惠安县| 杭锦旗| 东安县| 响水县| 玉树县| 五原县| 平南县| 洪泽县| 绿春县| 景德镇市| 沁水县| 绥江县| 新营市| 和林格尔县| 潜江市| 广西| 塔河县|