1. Go中的一些優(yōu)化筆記,簡約而不簡單

        共 3180字,需瀏覽 7分鐘

         ·

        2022-07-17 17:30

        今天小土給大家?guī)硪黄P于 Golang 項目中最簡單的優(yōu)化的文章。原文見 Golang: simple optimization notes[1]

        我們這里簡單聊一下優(yōu)化本身,然后我們直接從實際的示例開始。

        為什么要優(yōu)化呢?

        當你資源占有較高的話會需要很大的成本,雖然現在服務器資源也不是很貴,但是你還是需要針對的做一些優(yōu)化工作。

        另外每個優(yōu)化應該建立在一個benchmark的基礎上,需要體現它給我們帶來多大的收益。

        下面主要從slice、string、struct、function、map、interface、channel、pointer等方面羅列了一些常見的優(yōu)化點。

        數組和slice優(yōu)化篇

        提前為slice分配內存

        盡量使用第三個參數: make([]T, 0, len)

        如果你事先不知道確切的數量并且slice是臨時的,你可以設置得大一些,只要slice在運行時不會增長。

        不要忘記使用“copy”

        我們盡量不要在復制時使用 append,例如,在合并兩個或多個slice時。

        正確地使用迭代

        如果我們有一個包含很多元素或比較大的元素的slice,我們會嘗試使用“for”或 range 單個元素。通過這種方法,可以避免不必要的復制。

        學會復用slice

        如果我們需要對傳入的slice進行某種操作并返回結果,我們可以直接return,但已經修改了。這樣我們就可以避免了新的內存分配。

        不要留下未使用的slice

        如果我們需要從slice中切下一小塊并僅使用它,其實主要部分也會保留下來??梢允褂胏opy產生一個新的slice,而舊的對象讓GC回收。

        string-字符串優(yōu)化篇

        正確地進行拼接

        如果拼接字符串可以在一個語句中完成,那么可以使用“+”,如果需要在循環(huán)中執(zhí)行此操作,那么可以使用string.Builder。通過“Grow”也可以預先指定builder的大小。

        使用轉換優(yōu)化

        由于字符串是由字節(jié)組成的,因此有時這兩種類型之間的轉換可以避免內存分配。

        使用池化技術

        我們可以池化字符串,從而幫助編譯器只存儲一次相同的字符串。

        避免內存分配

        我們可以使用map來替代復合鍵,我們也可以使用[]byte。盡量不要使用fmt包,因為它的所有函數都使用了反射。

        struct-結構體優(yōu)化篇

        避免復制大的struct

        我們理解的小struct,是指不超過 4 個字段的struct,不超過一個機器字

        標準的copy案例

        • 轉換成interface
        • 接收和發(fā)送到channel
        • 替換map中的item
        • 向slice添加元素
        • 迭代(range)

        避免通過指針來訪問struct中的字段

        解引用是比較昂貴的,我們可以盡量少做,尤其是在循環(huán)中。我們也會失去使用快速寄存器的能力。

        使用小型的struct

        這項工作由編譯器優(yōu)化的,這意味著它的工作量很小。

        通過內存對齊來減小struct大小

        我們可以對齊struct(根據字段的大小,以正確的順序排列),從而可以減小struct本身的大小。

        func-函數優(yōu)化篇

        使用內聯函數或自己內聯

        我們盡量編寫一些可供編譯器內聯的小函數——它很快,但自己從函數中嵌入代碼則更快。對于熱路徑函數尤其如此。

        什么情況下不會被內聯?

        • recovery 函數
        • select
        • 類型聲明
        • defer
        • goroutine
        • for-range

        明智地選擇你的函數參數

        我們盡量使用“小”參數,因為它們的拷貝會被特別優(yōu)化。我們也嘗試在拷貝和GC的負載的與增長堆棧之間保持平衡。避免使用大量的參數——讓你的程序使用超快速的寄存器(寄存器的數量是有限的)

        聲明一個命名好的return結果

        這似乎比在函數體中聲明這些變量更高效。

        保存函數中間的結果

        幫助編譯器優(yōu)化你的代碼,保存中間結果,然后會有更多的選擇來優(yōu)化你的代碼。

        謹慎使用“defer”

        盡量不要使用 defer,或者至少 不要在循環(huán)中使用defer 。

        為“hot path”提供便利

        避免在這些地方分配內存,尤其是短期對象。首先要檢查的的就是最常見的分支(if,switch)。

        這里 hot path在Go源碼中[2]也出現多次,根據在 sync.Once 的上下文中,“hot path”是什么意思?[3]中的回答,這里翻譯為熱路徑是非常頻繁執(zhí)行的指令序列。

        map優(yōu)化篇

        提前分配內存

        一切都和其他地方一樣。初始化map時,指定其大小。

        使用空結構作為值

        struct{}什么都不是,因此例如對信號值使用這種方法是非常有益的。

        清空map

        map只能增長,不能縮小。我們需要控制這一點——完全而明確地重置map。因為刪除其所有元素無濟于事。

        盡量不要在鍵和值中使用指針

        如果 map 不包含指針,那么 GC 就不會在它上面浪費寶貴的時間。而且要知道字符串也是指針——使用[]byte而不是字符串作為鍵。

        減少更改的次數

        同樣,我們不想使用指針,但我們可以使用 map 和 slice 的復合體,并將鍵存儲在 map 中,將可以不受限制地更改的值存儲在slice中。

        interface優(yōu)化篇

        計算內存分配

        請記住,要給一個接口賦值,你首先需要將其拷貝到某處,然后粘貼一個指針。關鍵字是拷貝。事實證明,裝箱和拆箱的成本將近似于結構體的大小和一次分配。

        選擇最佳的類型

        在某些情況下,裝箱/拆箱期間不會進行內存分配。例如,比較小的和布爾值的變量和常量、具有一個簡單字段的struct、指針(包括map、chan、func

        避免內存分配

        與其他地方一樣,我們盡量避免不必要的內存分配。例如,將一個接口分配給一個接口,而不是裝箱兩次。

        僅在需要時使用

        避免在小型、頻繁調用的函數的參數和結果中使用接口。我們不需要額外的包裝和拆包。減少使用接口方法調用的頻率,哪怕只是因為它可以防止內聯。

        指針、chan、BCE(Bounds Check Elimination-邊界檢查) 優(yōu)化篇

        避免不必要的解引用

        尤其是在循環(huán)中,因為事實證明它太昂貴了。解引用是我們不想以犧牲自己為代價執(zhí)行的一系列必要操作。

        channel使用效率是低效的

        使用channel會比其他同步方法慢。另外,select 中的 case 越多,我們的程序就越慢。但是select、case + default是優(yōu)化過了的。

        盡量避免不必要的邊界檢查

        這也很昂貴,我們應該盡一切可能避免它。例如,一次檢查(獲?。┳畲髎lice索引比多次檢查更正確。最好是立即嘗試獲得極端的選項。

        總結

        在這篇文章中,我們看到了一些相同的優(yōu)化規(guī)則。

        幫助編譯器做出正確的決定。在編譯時分配內存,使用中間結果,并盡量保持代碼的可讀性。

        不要忘記使用內置的分析和trace跟蹤工具。

        最后小土也祝你在優(yōu)化的路上做到盡善盡美。

        參考資料

        [1]

        Golang: simple optimization notes: https://medium.com/scum-gazeta/golang-simple-optimization-notes-70bc64673980

        [2]

        hot path在Go源碼中: https://cs.opensource.google/search?q=%22hot%20path%22&ss=go%2Fgo

        [3]

        在 sync.Once 的上下文中,“hot path”是什么意思?: https://stackoverflow.com/questions/59174176/what-does-hot-path-mean-in-the-context-of-sync-once



        推薦閱讀


        福利

        我為大家整理了一份從入門到進階的Go學習資料禮包,包含學習建議:入門看什么,進階看什么。關注公眾號 「polarisxu」,回復 ebook 獲取;還可以回復「進群」,和數萬 Gopher 交流學習。

        瀏覽 19
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 国内自拍激情视频 | 青春期13分钟床戏被删减片段 | 日韩性无码| 国产精品操b | 美女污视频网站 |