這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
本文為部分翻譯、整理。 原文為Go的開發人員之一的Dave Cheney所做的 Five things that make gofast
清晰賦實值型別
例如,有一個絕對不會超過uint32的數值,就不要用int var gocon uint32 = 2015 這樣gocon這個值只會佔用4個位元組為啥呢? 因為如,CPU的處理速度已經遠超記憶體的匯流排速度了 所以數值能用小的用小的,盡量讓數值留在CPUcache,而不是速度更慢的記憶體裡
函數調用有overhead,為了內聯,盡量消除編譯器無法偵測的dead code
當函數調用時,始終是由overhead(額外開銷)的,比如儲存調用棧,CPU切出。因此編譯器會嘗試進行內聯,將小函數直接複製並編譯。舉個栗子:
func Max(a,b int) int { if a > b { return a } return b}func DoubleMax(a, b) int { return 2 * Max(a,b)}
-m 查看內聯狀態 $go build -gcflags=-m main.go # utils src/utils/max.go:4: caninline Max src/utils/max.go:11: inlining call to Max這樣做的代價是可執行檔二進位檔案更大了,但由於內聯,並不是函數調用,效能自然是更好了。 但有些函數,是不能內聯的,比如下面這個
func Test() bool {return False}func Expensive() { if test(){ //接下來的Expensive沒辦法內聯 // Expensive.... }}
改成下面這樣就可以內聯了
const TEST = Falsefunc Expensive() { if TEST{ // Expensive.... }}
逃逸檢查
首先,要理解一個概念,stack 和 heap stack範圍是本地的(locals),在函數執行完之後會自動收回,CPU控制,效率高 而heap則需要由程式來管理,效率低 具體有篇文章講這個:Memory stack vsheap因此,就算有GC,也應該把不需要傳出的參數盡量控制在函數內。 例如的程式 因為numbers 只在Sum中,編譯器也會自動分配100個int空間在stack中,而不是heap中。 正因為在stack 中,所以不需要GC參與,自動收回。但這不意味著不能用指標引用,見第二個栗子: 儘管變數c是通過new函數產生的,但是因為在center外沒有c的引用,所以c也會被儲存在stack上。 逃逸檢查執行個體:
Goroutine
第四個,我覺得是提示吧: Goroutine是比進程、線程都小的執行單元 Goroutine中的會被調度器(scheduler)切出的操作:
- chan 收發
- go 語句調用函數
- 阻塞的syscall
- gc
一圖勝千言,表示了調度器是如何在goroutine之間切換的。 第五個算是對Go1.3以後的stack分配機制的總結,我就不翻譯了,有興趣的同學自己可以看看原文:)
總結
- 我想補充的是: 逃逸對於channel也是成立的,因此,在channel之間,最好傳遞的也是對象,而不是引用。這個問題上我栽過一次了哈哈哈
- 之前我並不知道內聯是啥,Golang、pypy這樣的JIT、或者cpython真的是減輕了我的心智負擔,當然,瞭解一下也是很不錯的。
- 清晰的賦值個人感覺不是很必要,因為Go的int類型最大是2**31-1,效能調優的時候再認真地梳理其實也來得及