合理設(shè)置gomaxprocs可避免過度并行化,1.別盲目設(shè)高值,4核cpu設(shè)8反而增加切換開銷;2.適合i/o密集型任務(wù)適當(dāng)提高以掩蓋延遲,建議先用默認(rèn)值測試再調(diào)整。3.工作竊取策略通過本地隊列減少鎖競爭,空閑邏輯處理器隨機(jī)“偷”任務(wù)平衡負(fù)載。4.復(fù)用協(xié)程池、控制并發(fā)粒度、避免阻塞操作可減少調(diào)度開銷。
在go語言中,協(xié)程(goroutine)調(diào)度的高效性是其并發(fā)性能優(yōu)越的重要原因之一。但隨著并發(fā)量的增加,協(xié)程調(diào)度本身也會帶來一定的開銷。優(yōu)化這部分開銷,關(guān)鍵在于理解并合理使用GOMAXPROCS設(shè)置和工作竊取策略。
控制GOMAXPROCS:別盲目追求最大值
GOMAXPROCS決定了Go程序可以同時運(yùn)行的邏輯處理器數(shù)量,也就是能并行執(zhí)行協(xié)程的CPU核心數(shù)。默認(rèn)情況下,從Go 1.5版本開始,它會自動設(shè)置為機(jī)器的CPU核心數(shù)。很多人誤以為把這個值調(diào)得越高,性能越好,其實(shí)不然。
- 高GOMAXPROCS可能帶來更多上下文切換:比如你有4核CPU,設(shè)置GOMAXPROCS為8,并不會提升性能,反而可能導(dǎo)致線程頻繁切換,浪費(fèi)資源。
- 適合I/O密集型任務(wù)的場景:如果你的應(yīng)用大量依賴網(wǎng)絡(luò)請求或磁盤讀寫,適當(dāng)提高GOMAXPROCS有助于掩蓋I/O延遲。
- 建議做法:先保持默認(rèn)值觀察性能表現(xiàn),再根據(jù)實(shí)際負(fù)載測試調(diào)整,不要超過物理核心數(shù)太多。
可以通過如下方式手動控制:
立即學(xué)習(xí)“go語言免費(fèi)學(xué)習(xí)筆記(深入)”;
runtime.GOMAXPROCS(4)
工作竊取策略:平衡負(fù)載的秘密武器
Go調(diào)度器采用的是“工作竊取”機(jī)制來平衡不同處理器之間的協(xié)程負(fù)載。簡單來說,當(dāng)一個邏輯處理器的任務(wù)隊列空了,它會去其他處理器那里“偷”一些任務(wù)來執(zhí)行。
- 本地隊列 + 全局隊列結(jié)合:每個P(邏輯處理器)都有自己的本地運(yùn)行隊列,減少鎖競爭;全局隊列用于初始化和調(diào)度器回收任務(wù)。
- 竊取策略通常是隨機(jī)選擇:不是輪流找別人要任務(wù),而是隨機(jī)挑一個目標(biāo)去“偷”,這減少了多個P同時爭搶同一目標(biāo)的可能性。
- 對開發(fā)者透明,但影響性能:我們不需要手動干預(yù)這個過程,但可以通過避免協(xié)程長時間阻塞、盡量讓任務(wù)分布均勻來幫助調(diào)度器更好地工作。
減少協(xié)程創(chuàng)建與銷毀的開銷
雖然goroutine比線程輕量得多,但也不是完全沒有成本。尤其是在短時間內(nèi)頻繁創(chuàng)建大量協(xié)程時,可能會造成內(nèi)存壓力和調(diào)度負(fù)擔(dān)。
- 復(fù)用goroutine池:使用類似ants這樣的第三方協(xié)程池庫,可以避免重復(fù)創(chuàng)建銷毀帶來的開銷。
- 控制并發(fā)粒度:比如批量處理數(shù)據(jù)時,不要每個元素都起一個goroutine,而是按塊劃分,或者使用帶緩沖的channel限制并發(fā)數(shù)量。
- 注意阻塞操作:像死循環(huán)或長時間sleep的協(xié)程會占用P資源,影響整體調(diào)度效率,必要時可以用runtime.Gosched()主動讓出時間片。
總結(jié)一下
合理設(shè)置GOMAXPROCS可以避免過度并行化帶來的副作用;工作竊取機(jī)制雖然不需我們干預(yù),但了解其原理有助于寫出更利于調(diào)度的代碼;而減少不必要的協(xié)程創(chuàng)建,也是優(yōu)化調(diào)度開銷的重要手段之一。這些細(xì)節(jié)看起來不復(fù)雜,但在高并發(fā)場景下容易被忽略。