當前位置:
首頁 > 最新 > 如何做 Go 的性能優化?

如何做 Go 的性能優化?

Go的性能優化其實總的來說和C/C++等這些都差不多,但也有它自己獨有的排查方法和陷阱,這些都來源於它的語言特性和環境。

1.性能優化前提——任何好的東西都是在正確的前提上

代碼界的很多事是和我們生活的哲學息息相關的,我們想要做好一件事,首先要保證我們能按時完成我們的任務,其次再去想如何把工作做的更好。如果一味只去要求做的盡善盡美可能會導致延期,失敗,半途而廢。

所以,先寫正確的代碼,再去考慮如何去讓代碼更快更好的運行;先完成基本的功能,再去想如何優化它。正確是優化的基礎,沒有這個基礎,任何的優化都是毫無意義的。

2.性能優化限制——架構設計和硬體資源

良好的架構設計是我們能夠發揮性能的前提,一個設計不當的架構付出再多精力優化效果也是大打折扣。這也是我們為什麼經常會看到隨著業務量或者用戶數的增加後天架構會不斷演進變化,如果說一開始設計的架構可以一直支撐下去,那麼請大神請收下我的膝蓋!

硬體資源更好理解,一個16核,64G內存的伺服器和4核,4G內存的垃圾機器對比簡直是天與地。毋庸多說。

3.什麼時候做性能優化

We should forget about small efficiencies, say about 97% of the time; premature optimization is the root of all evil(大概97%的時間,我們應該忘記小的優化, 過早優化是所有邪惡的根源). —— Donald E.Knuth

這句話不是說不去優化,不去思考演算法,而是在早期我們應該更加專註於程序的實現,而不是一開始就去想著優化,你大可以放開去寫。慢慢的會有驅動力讓我們不自覺去優化。

正常情況下這種驅動我覺得有兩種,一種是自我驅動,比如經歷過搞過ACM或者演算法競賽的童鞋們在面對一個問題時會不自覺地從複雜度角度分析問題;或者一個「強迫症患者」不能忍受慢,卡,崩等等情況。

另一種是環境驅動,比如高並發環境,高精度環境,低延遲環境,大數據環境等等對於我們系統的某一方面甚至多個方面都有苛刻的要求,逼這著我們需要不斷優(jia)化(ban),優(jia)化(ban),再優(jia)化(ban)。

當你意識到這個函數可能會被經常調用,就需要想辦法的優化

當你意識到這個數據結構設計不合理導致內存佔用過高,就需要想辦法優化

當用戶反映服務響應太慢,就需要優化

當老闆既要好的服務又不想再花錢買機器,就需要優化

當代碼太亂,問題百出,經常報警告打擾和女票玩耍,就需要優化

。。。

4.花多長時間來做性能優化

有人說是二八定律,被廣泛應用於社會學及企業管理學等。是19世紀末20世紀初義大利經濟學家巴萊多發現的。他認為,在任何一組東西中,最重要的只佔其中一小部分,約20%,其餘80%儘管是多數,卻是次要的,因此又稱二八定律。—— 百度百科

我覺得雖然我們不必一定按照二比八的要求去執行,但毫無疑問的是優化會耗費我們非常多的時間和精力,並且遠遠大於我們系統實現的時間,或者說自從第一次開發完,以後所有的時間都是在做優化。自己的曾經的經歷,當時為了給某銀行做60W終端測試優化一個API緩存系統,基本功能實現兩周就完成了,後面我和性能QA童鞋一波波優化——測試——優化——測試,花費了一個多月時間做這件事,這還沒完,後面在真實環境測試過程仍然暴露了很多問題,例如goruntine暴增積壓,CPU暴增等等,後來發現是架構設計和組件使用上的問題,是的,當出現這樣的問題時不是不可以解決,但是為了解決這樣的問題會把系統搞的複雜,臃腫,雖然開發經驗不多,但我覺得該是代碼實現的代碼實現,該是組件解決的問題就應該組件來解決,架構設計問題就是架構需要改進,不要說都可以在代碼中解決,除非是不得已。

5.工欲善其事,必先利其器

首先是代碼層次,好的代碼是性能的關鍵因素,實現函數效率怎麼樣,排序是不是高效,操作並發性高不高等等,你可以使用代碼質量評估工具來做評估,當然最好還是讓有經驗的司機們手把手指導。

Go代碼評估工具:

然後是如何在運行過程來調試Go程序,Go自帶了一個pprof工具,這個工具可以做CPU和內存的profiling。使用可以參考之前介紹文章:一個內部API系統的性能優化 - 知乎專欄

然後配合工具生成流程圖,佔比圖清晰明了。

或者對於一些程序你還可以在運行時去改變它,調試它,你可以去查看棧,內存,CPU,heap等等信息,很不錯,但是我不喜歡它開啟了服務埠,這個項目剛開始是不需要使用新的埠,直接使用套接字文件通信,但是因為無法在windows上實現,最後作罷,從此好感降低了!google/gops

當然你還可以使用GDB工具,最新的GDB貌似還加入了查看goruntine的命令,很棒!

6.演算法與優化思路

這個不用多說,說實話個人覺得演算法是區分工程師和碼農的一個很大分界點,演算法可以說是基本能力,很多人不以為然覺得只是面試門檻,但是看看代碼實現中數據結構的設計和演算法實現就明白了!當遇到問題會不自覺的想到一個演算法,這個目的就夠了,其實並沒有說演算法非常牛逼,其實之前老司機聊天也說過只要你能在遇到問題能夠想到用什麼演算法解決即可。

各種排序,集合操作,查詢等等,沒有最好的演算法,只要最適合的演算法

至於哪些方面需要優化,一方面是演算法的效率還要就是現象,例如CPU特別高那麼看看goruntine的調度,哪個函數佔用比高,是不是存在死循環;內存大,看看是不是有大的內存分配沒有及時回收,或者是不是有頻繁的內存分配,是不是有內存泄露?響應慢是卡在哪裡,是執行效率還是和組件通信等等。

7.Go的陷阱與技巧

a.make的陷阱

func main() {

s := make([]int, 3)

s = append(s, 1, 2, 3)

fmt.Println(s) } 結果 [0 0 0 1 2 3]

b.map讀寫衝突,產生競態

c.文件打開,資料庫連接記得一定要關閉或釋放,一般使用defer

d.對於一個struct值的map,你無法更新單個的struct值

e.簡化range

for range m { }

f.defer的陷阱

有名返回值則是在函數聲明的同時就已經被聲明,匿名返回值是在return執行時被聲明,所以在defer語句中只能訪問有名返回值,而不能直接訪問匿名返回值。

package main import (

"fmt" ) func main() {

fmt.Println("return:", defer_call()) } func defer_call() int {

var i int

defer func() {

i++

fmt.Println("defer1:", i)

}()

fmt.Println("defer2:", i)

return i } defer2: 1 defer1: 2 return: 0

Q2.

fmt.Println("return:", defer_call()) } func defer_call() (i int) {

return i } defer2: 1 defer1: 2 return: 2

g.短式變數聲明的陷阱

那些使用過動態語言的開發者而言對於短式變數聲明的語法很熟悉,所以很容易讓人把它當成一個正常的分配操作。這個錯誤,將不會出現編譯錯誤,但將不會達到你預期的效果。

這個說到底是代碼邊界和變數影響範圍問題。

h.nil和顯式類型

nil標誌符用於表示interface、函數、maps、slices和channels的「零值」。如果你不指定變數的類型,編譯器將無法編譯你的代碼,因為它不知道具體的類型,同時你也不能給string賦nil值。

應該

i.全部是值傳遞,沒有引用傳遞

如果你是一個C或則C++開發者,那麼知道數組就是指針。當你向函數中傳遞數組時,函數會參照相同的內存區域,這樣它們就可以修改原始的數據。但Go中的數組是數值,因此當你向函數中傳遞數組時,函數會得到原始數組數據的一份複製。如果你打算更新數組的數據,你將會失敗。

j.select下的所有case遍歷是隨機的,在使用的過程中要注意,這和switch是不同的

l.使用介面實現一個類型分類函數:

func classifier(items ...interface{}) { for i, x := range items { switch x.(type) { case bool: fmt.Printf("param #%d is a bool", i) case float64: fmt.Printf("param #%d is a float64", i) case int, int64: fmt.Printf("param #%d is an int", i) case nil: fmt.Printf("param #%d is nil", i) case string: fmt.Printf("param #%d is a string", i) default: fmt.Printf("param #%d』s type is unknown", i) } } }

l.Map值在獲取的時候是無序的,所以當我們需要有序時就需要通過字元串數組排序間接得到

package main import ( "fmt" "sort" ) func main() { var m = map[string]int{ "unix": 0, "python": 1, "go": 2, "javascript": 3, "testing": 4, "philosophy": 5, "startups": 6, "productivity": 7, "hn": 8, "reddit": 9, "C++": 10, } var keys []string for k := range m { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Println("Key:", k, "Value:", m[k]) } }

m.init函數

開發過程我們經常會遇到主要邏輯開始前要聲明或者一些全局的變數或者初始化操作,或者有時候我們僅僅需要import一些包,並不需要使用裡面的函數,那就需要使用init初始化函數,一個package中可以有多個init。

n.Go程序顯示佔用內存有時候並不是真正在用的內存,只是還沒還給操作系統

o.雨痕老師的研究

p.Golang的五十度灰

中文版:Go的50度灰:Golang新開發者要注意的陷阱和常見錯誤

英文版:50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs

後續再補充。。。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 推酷 的精彩文章:

Xcode代碼全黑的另一種解決辦法
微軟推出了一款 App 可以幫你讀出全世界
PSAttack:一個包含所有的滲透測試用例的攻擊型Powershell腳本框架
一個靈活的AssetBundle打包工具
GraphQL和REST對比時需要注意些什麼

TAG:推酷 |

您可能感興趣

身為QA,你是否也了解SQL性能優化?
性能or便攜 遊戲本輕薄化是必然的結果嗎?
iOS性能優化探討
什麼是速射炮?它的性能優勢怎麼樣的呢?
模型不收斂,訓練速度慢,如何才能改善 GAN 的性能?
iOS 性能優化探索
vivo NEX系統更新:優化指紋性能和觸屏功能
Android 性能優化之內存優化
它與airpods一樣的好用,性能卻更強悍!魅族POP真無線耳機體驗
Web 前端性能優化
蘇寧Nodejs性能優化實戰
NS性能不好不想買?只看性能的評論都是耍流氓
關於Oracle資料庫性能優化,你可能已經走了彎路!
nginx如何實現高性能和可擴展性
T50性能如何?是不是真的很差?隱身能力不足,機動性超然
蘇聯的MT-LB履帶輸送車性能如何?性能一般,但是改改還能用
如何查詢iPhone電池健康:優化手機性能
拿著升降攝像頭的它出去飆性能虛不虛?vivo NEX性能及遊戲評測
華為的「GPU Turbo」技術解釋,手機是如何大幅提升圖形性能的?
印度裝備的英薩斯INSAS步槍性能具體怎麼樣?性能奇差,無彈可用