GMP 爲什麼要有 P ?

一個協程得以運行,需要同時滿足以下兩個條件:

  1. P 已經和某個線程進行綁定,這樣才能參考操作系統的調度獲得 CPU 時間
  2. P 已經從隊列中(可以是本地隊列,也可以是全局隊列,甚至是從其他 P 的隊列)取到該協程

第一個條件就是 操作系統調度,而第二個其實就是 Go 裏的調度器

操作系統調度

假設一臺機器上有兩個 CPU 核心,意味着,同時在同一時間裏,只能有兩個線程運行着。

可如果該機器上實際開啓了 4 個線程,要是先執行一個線程完再執行另一個線程,那麼當某一個線程因爲一些阻塞性的系統調用而阻塞時,CPU 的時間就會因此而白白浪費掉了。

更合適的做法是,使用 操作系統調度策略,設定一個調度週期,假設是 10ms (毫秒),那在一個週期裏,每個線程都平均分,都只能得到 2.5ms 的CPU 運行時間。

可如果機器上有 1000 個線程呢?難道每個線程都分個 0.01 ms (也就是 10 微秒)嗎?

要知道,CPU 從 A 線程切換到 B 線程,是有巨大的時間浪費在線程上下文的切換,如果切換得太頻繁,就會有大量的 CPU 時間白白浪費。

Go 語言面試題 100 講之 017篇:說一下 GMP 模型的原理

因此,通常會限制最小的時間片的長度,假設爲 2ms,受此調整,現在調度週期就會變成 2*1000 = 2s 。

 

GM 模型是怎樣的?

在 Go v1.1 之前,實際上 GMP確實是沒有 P 的,所有的 M 線程都要從 全局隊列中獲取 G 來執行任務,爲了避免衝突,從全局隊列中獲取 G 的時候,要先獲取一把大鎖。

當一個程序的併發量比較小的時候,影響還不大,而當程序的併發量非常大的時候,這個全局隊列會成爲性能的瓶頸。

除此之外 ,若直接把 G 從全局隊列分配給 M,那麼當 G 中當生系統調用或者其他阻塞性的操作時,M 會有一段時間處於掛起的狀態,此時又沒有新創建線程的線程來代替該線程繼續從隊列中取出其他 G 來運行,從效率上其實會打折扣。

P 帶來的改變

加了 P 之後會帶來什麼改變呢?

  • 每個 P 有自己的本地隊列,大幅度的減輕了對全局隊列的直接依賴,所帶來的效果就是鎖競爭的減少。而 GM 模型的性能開銷大頭就是鎖競爭。
  • 當一個 M 中 運行的 G 發生阻塞性操作時,P 會重新選擇一個 M,若沒有 M 就新創建一個 M 來繼續從 P 本地隊列中取 G 來執行,提高運行效率。
  • 每個 P 相對的平衡上,在 GMP 模型中也實現了 Work Stealing 算法,如果 P 的本地隊列爲空,則會從全局隊列或其他 P 的本地隊列中竊取可運行的 G 來運行,減少空轉,提高了資源利用率。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章