這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
原文在此:http://en.munknex.net/2011/12/golang-goroutines-performance.html
————————–翻譯分割線————————–
概述
在這篇文章裡,我將嘗試評估 goroutine 的效能。goroutine 是類似輕量級線程的東西。為了提供原生的多任務,它(協同 channel 一起)被內建於Go中。
文檔告訴我們:
它實際上是在同一個地址空間裡建立成百上千個 goroutine。
因此,這個文章的重點就是測試並明確在如此巨大的並發運行函數的情況下所能承受的效能壓力上限。
記憶體
建立一個新的goroutine所需空間並未記錄在文檔中。只是說需要幾KB。在不同的機制下測試,協助確認這個數值為4—4.5kB。因此,5GB差不多足夠運行一百萬個goroutine。
效能
讓我們算算在一個 goroutine 裡運行函數會損失多少的效能吧。可能你已經知道這非常簡單——只要在函數調用前添加 go 關鍵字:
go testFunc()
goroutine 複用於線程。預設情況下,如果沒有設定 GOMAXPROCS 環境變數,程式只使用一個線程。為了利用全部 CPU 核心,則必須制定它的值。例如:
export GOMAXPROCS=2
這個值在運行時使用。因此沒有必要在每次修改這個值之後,重新編譯器。
以我的推斷,大多數時間花費在建立 goroutine,切換它們,以及從一個線程遷移 goroutine 到另外的線程,還有在不同的線程之間的 goroutine 進行通訊。為了避免無盡的論述,讓我們從僅用一個線程的情況開始。
所有測試都是在我的 nettop(譯註:英特爾公司的低成本簡易台式機解決方案)上完成的:
- Atom D525 Dual Core 1.8 GHz
- 4Gb DDR3
- Go r60.3
- Arch Linux x86_64
方法
這是測試函數產生器:
func genTest (n int) func (res chan <- interface {}) { return func(res chan <- interface {}) { for i := 0; i < n; i++ { math.Sqrt(13) } res <- true }}
然後這裡是一系列分別計算 sqrt(13) 1、10、100、1000 和 5000 次的函數集合:
testFuncs := [] func (chan <- interface {}) { genTest(1), genTest(10), genTest(100), genTest(1000), genTest(5000) }
我將每個函數在迴圈中執行 X 遍,然後在 goroutine 中執行 X 遍。然後比較結果。當然,應當留意記憶體回收。為了降低其帶來的影響,我在 goroutine 結束後顯式調用了 runtime.GC() 並記錄結束時間。當然,為了測試精確性,每個測試執行了許多遍。整個已耗用時間用了大約 16 小時。
一個線程
export GOMAXPROCS=1
圖表顯示在 goroutine 中啟動並執行 sqrt() 計算工作大約比在函數中運行慢四倍。
來看看剩餘的四個函數的情況:
你會注意到,即使並發執行70萬 goroutine 也不會使得效能下降到 80% 以下。現在是最值得敬佩的地方。從 sqrt()x1000 開始,總體消耗低於 2%。5000 次——只有 1%。看起來這個數值與 goroutine 的數量無關!因此唯一的制約因素是記憶體。
概要:
如果互不依賴的代碼的執行時間高於計算平方跟的10倍,並且你希望它並發執行,應毫不猶豫的讓其在 goroutine 中運行。雖然,可以輕鬆的將10或者100個這樣的代碼放在一起,不過損失的效能僅僅分別是 20% 和 2%。
多線程
現在來看看當我們希望使用若干個處理器核心時的情況。在我的用例中是 2 個:
export GOMAXPROCS=2
再次執行我們的測試程式:
這裡你會發現,儘管核心數增加了一倍,但是前兩個函數的已耗用時間卻增加了!這極可能是線上程之間移動比執行它們的開銷要大得多。:)當前的調度器還不能處理,不過 Go 的開發人員承諾在未來會解決這種情況。
你已經看到了,最後的兩個函數完全使用了兩個核。在我的 nettop 上,他們的執行時間分別是 ~45µs 和 ~230µs。
總結
儘管這是一個年輕的語言,並且有著一個臨時的調度器實現,goroutine 的效能讓人覺得興奮。尤其是與 Go 的簡單易用結合起來的時候。這令我印象深刻。感謝 Go Team Dev!
當已耗用時間少於 1µs 我會在執行 goroutine 之前深思熟慮一下,而如果已耗用時間超過 1ms,那就決不猶豫的使用 goroutine 了。:)