Go Ballast(通過嘗試降低 GC 頻率以提高整體性能,針對所有 Go應用都適用)
首先我們明白GO語言GC觸發條件是由比例來觸發的。例如,當前存活內存10GB,觸發比例是100%,因此下次觸發GC的時候是當內存達到20GB的時候觸發GC。這種機制在當前小內存的情況下會平凡觸發GC,例如當前只有1GC的話則內存達到2GB就要觸發GC。而單純提高比例到1000%,那么下次觸發GC的時候實際上內存已經有較大的壓力。所以,我們采用Ballast機制。
Ballast機制在小內存的時候會申請一些內存,已達到延緩下一次GC的時間。具體的,也就一行代碼:
// allocate 2GB ballast which resident in virtual memory only
ballastObject := make([]byte,1024*1024*1024*2)
首先,內存是在續存中分配,所以實際上我們是申請了這塊內存但沒映射到物理內存。
假設除 Ballast 以外的存活對象總大小為X字節(平均值),Ballast 對象大小為 B字節
1.Ballat 最多會帶來大約 B 字節的額外物理內存使用(值得注意的是,這部分多占用的物理內存依然是被除 Ballast 以外的對象所使用,不存在浪費問題,Ballast 本身僅僅存在于虛擬內存中,不會被實際地映射物理頁面)
2 Ballast能帶來性能優化的根本原因是降低了GC頻率
根據1得到:不管大內存還是小內存場景,Ballast 都會額外帶來最大為 B 字節的物理內存使用。
根據2得到:在大內存場景即X比較大時,一般來說此時 GC頻率會比較小,所以 Ballast帶來的優化效果不會像 X 為較小值時那么明顯。
那么這樣做在清理內存是否會占用更多時間?
答案:不會。GC主要是mark 和sweep。其中sweep速度很快,幾乎不暫用時間。在mark階段由于Ballast不是存活對像,所以不會被掃描到,因此幾乎也不占用時間。
CPUWorker(嘗試給 Go 帶來一個類似內核 CFS 調度器的 goroutime 調度器,以通過其提供的優先級機制保證關鍵goroutine 的延遲指標,目前還沒有在TiDB中嘗試
應用,Demo效果很好).
在linux中的調度算法采用CFS算法。CFS算法在休眠進程在喚醒時會獲得vruntime的補償(減少vruntime,提高這個進程優先級),它在醒來的時候有能力搶占CPU是大概率事件,這也是CFS調度算法的本意,即保證交互式進程的響應速度,因為交互式進程等待用戶輸入會頻繁休眠。
- 計算密集型作業將運行很長時間,因此它將優先級放后;
- I/O密集型作業會運行很短的時間,因此它只會稍微放后移動;
而goruntime調度器采用的是RR方式。這對IO密集型作業很不友好,交互式進程交互能力差。
解決方式:
1,使用CGO。但是CGO執行write的時候將會導致大內存復制。
2,再來一個并行的GOruntime實例進程。將go library編譯成CGO.so文件,然后通過我們GO的CGO來調用這個.so文件。所以這時候其實是有兩個隔開的進程也就是兩個GMP模型,所以原來的程序和這個IO密集型的.so文件的程序是不會互相影響的。
3,使用CPU worker。
https://docs.google.com/document/d/1g5SgpMg28XyMFdPTVMrZrZiLFBfAIgW8sMEXkKEQTzE/edit