Go并發(fā)&調(diào)度親和性

【譯文】原文地址
將goroutine從一個(gè)操作系統(tǒng)線程切換到另一個(gè)線程是有代價(jià)的晨雳,如果切換太頻繁會(huì)降低應(yīng)用程序的速度唠亚。隨著Go發(fā)展饭弓,調(diào)度器已經(jīng)解決了這個(gè)問題废士。在并發(fā)工作時(shí)淋肾,調(diào)度器提供goroutine和線程的親和性硫麻。先回顧幾年前的調(diào)度器來理解這種改進(jìn)過程。

Go老版本存在的問題

在Go 1.0&1.1早期樊卓,當(dāng)創(chuàng)建更多os線程(即將GOMAXPROCS值設(shè)置更大)來運(yùn)行并發(fā)程序時(shí)將會(huì)面臨性能下降的問題拿愧。讓我們從文檔中使用channel來計(jì)算質(zhì)數(shù)的例子開始:

package main

import "fmt"

//Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch chan<- int)  {
    for i := 2; ;i++  {
        ch <- i
    }
}

//Copy the values from channel 'in' to channel 'out',
//removing those divisable by 'prime'.
func Filter(in <-chan int, out chan<- int, prime int)  {
    for{
        i := <-in  //Receive value from 'in'.
        if i % prime !=0 {
            out <- i  //send 'i' to 'out'.
        }
    }
}

// The prime sieve: Daisy-chain Filter processes.
func main() {
    ch := make(chan int)
    go Generate(ch)
    for i :=0; i <10; i++{
        prime := <-ch
        fmt.Println(prime)
        ch1 := make(chan int)
        go Filter(ch, ch1, prime)
        ch = ch1

    }

}

以下是Go 1.0.3版本,在不同GOMAXPROCS值下碌尔,計(jì)算10萬個(gè)質(zhì)數(shù)的基準(zhǔn)測(cè)試結(jié)果:

name     time/op
Sieve    19.2s ± 0%
Sieve-2  19.3s ± 0%
Sieve-4  20.4s ± 0%
Sieve-8  20.4s ± 0%

要理解這些結(jié)果浇辜,我們需要理解此時(shí)調(diào)度器是如何設(shè)計(jì)的券敌。在Go第一個(gè)版本中,調(diào)度器只有一個(gè)全局隊(duì)列柳洋,所有的線程都可以推送和獲取goroutines待诅。下面是一個(gè)應(yīng)用程序?qū)嵗搼?yīng)用程序最多運(yùn)行兩個(gè)操作系統(tǒng)線程M,通過設(shè)置GOMAXPROCS=2來實(shí)現(xiàn):


第一個(gè)版本調(diào)度器只要一個(gè)全局隊(duì)列

只有一個(gè)隊(duì)列并不能保證goroutine將在同一個(gè)線程上恢復(fù)執(zhí)行熊镣。第一個(gè)線程準(zhǔn)備就緒卑雁,會(huì)獲取一個(gè)等待的goroutine運(yùn)行。因此轧钓,這里就會(huì)涉及到goroutine從一個(gè)線程到另一個(gè)線程的切換序厉,在性能方面會(huì)產(chǎn)生消耗。下面是一個(gè)阻塞式channel例子:

  • G7協(xié)程阻塞在channel上毕箍,等待channel中發(fā)送來的數(shù)據(jù)弛房。一旦channel有數(shù)據(jù)可接收,該協(xié)程會(huì)被推送到全局隊(duì)列當(dāng)中而柑。


  • 然后文捶,channel推送消息,GX協(xié)程將在一個(gè)準(zhǔn)備就緒的線程上運(yùn)行媒咳,而G8協(xié)程將阻塞在channel上:


  • 此時(shí) G7協(xié)程就會(huì)被調(diào)度到該線程上去:



    Goroutine現(xiàn)在在不同的線程上運(yùn)行粹排。只有一個(gè)全局調(diào)度隊(duì)列會(huì)迫使調(diào)度程序只有一個(gè)互斥鎖來覆蓋所有的goroutine調(diào)度操作。以下是使用pprof工具獲取的CPU概況:

Total: 8679 samples
3700  42.6%  42.6%     3700  42.6% runtime.procyield
1055  12.2%  54.8%     1055  12.2% runtime.xchg
753   8.7%  63.5%     1590   18.3% runtime.chanrecv
677   7.8%  71.3%      677    7.8% dequeue
438   5.0%  76.3%      438    5.0% runtime.futex
367   4.2%  80.5%     5924   68.3% main.filter
234   2.7%  83.2%     5005   57.7% runtime.lock
230   2.7%  85.9%     3933   45.3% runtime.chansend
214   2.5%  88.4%      214    2.5% runtime.osyield
150   1.7%  90.1%      150    1.7% runtime.cas

procyield, xchg, futex和lock都與Go調(diào)度器的全局互斥量有關(guān)涩澡。很清楚的發(fā)現(xiàn)顽耳,應(yīng)用程序的很大部分時(shí)間花在鎖上。
這些問題導(dǎo)致Go在多處理器上沒有優(yōu)勢(shì)妙同,在Go1.1中已經(jīng)通過一個(gè)新的調(diào)度器解決了射富。

并發(fā)時(shí)的親和性

Go 1.1實(shí)現(xiàn)了一個(gè)新的調(diào)度器,并創(chuàng)建了本地調(diào)度隊(duì)列粥帚。如果有本地goroutines調(diào)度隊(duì)列并允許他們運(yùn)行在同一個(gè)OS線程上胰耗,這個(gè)改進(jìn)避免了鎖定整個(gè)調(diào)度程序。
由于線程可能在系統(tǒng)調(diào)用時(shí)阻塞芒涡,并且這種阻塞的線程數(shù)量是沒有限制的柴灯,Go引入了processes的概念。處理器P表示代表一個(gè)運(yùn)行的OS線程并管理本地goroutine調(diào)度隊(duì)列费尽。下面是新的模式:



如下是在Go 1.1.2版本使用新調(diào)度器運(yùn)行的基準(zhǔn)測(cè)試:

name     time/op
Sieve    18.7s ± 0%
Sieve-2  8.26s ± 0%
Sieve-4  3.30s ± 0%
Sieve-8  2.64s ± 0%

Go現(xiàn)在可充分利用所有可用的CPU赠群。CPU使用概況也發(fā)生變化:

Total: 630 samples
163  25.9%  25.9%      163  25.9% runtime.xchg
113  17.9%  43.8%      610  96.8% main.filter
93  14.8%  58.6%      265   42.1% runtime.chanrecv
87  13.8%  72.4%      206   32.7% runtime.chansend
72  11.4%  83.8%       72   11.4% dequeue
19   3.0%  86.8%       19    3.0% runtime.memcopy64
17   2.7%  89.5%      225   35.7% runtime.chansend1
16   2.5%  92.1%      280   44.4% runtime.chanrecv2
12   1.9%  94.0%      141   22.4% runtime.lock
9   1.4%  95.4%       98    15.6% runqput

與鎖相關(guān)的大部分操作都已刪除,標(biāo)記為chanXXXX的操作只與channels相關(guān)依啰。但是乎串,如果調(diào)度程序改進(jìn)了goroutine和線程之間的親和性,那么在某些情況下,這種親和性需要降低叹誉。

限制親和性

要了解親和性的限制鸯两,我們必須了解何時(shí)會(huì)進(jìn)入本地隊(duì)列和全局隊(duì)列。本地隊(duì)列將用于除了系統(tǒng)調(diào)用外的所有操作长豁,例如阻塞在通道上和select操作钧唐,以及等待計(jì)時(shí)器和鎖,goroutine都會(huì)進(jìn)入本地調(diào)度隊(duì)列匠襟。然而钝侠,有兩個(gè)特例可以限制goroutine和線程的親和性:

  • 工作竊取。當(dāng)處理器P在本地隊(duì)列沒有足夠的goroutine可調(diào)度酸舍,將會(huì)從其他P中竊取帅韧,并且全局隊(duì)列和網(wǎng)絡(luò)輪詢都為空。被竊取的goroutine就會(huì)在別的線程執(zhí)行啃勉。
  • 系統(tǒng)調(diào)用忽舟。當(dāng)發(fā)生系統(tǒng)調(diào)用(如文件操作,http調(diào)用淮阐,數(shù)據(jù)庫(kù)操作等)叮阅,Go以阻塞模式掛起正在運(yùn)行的os線程,讓新的線程來處理當(dāng)前P上的本地隊(duì)列泣特。
    但是浩姥,為了更好地管理本地隊(duì)列優(yōu)先級(jí),以上兩個(gè)約束可以避免状您。Go 1.5為了給goroutine在channel上來回通信提供更多的優(yōu)先級(jí)勒叠,因此通過指定線程以優(yōu)化親和性。

排序來提高親和性

goroutine在通道上來回通信導(dǎo)致頻繁的阻塞膏孟,例如頻繁在本地隊(duì)列中排隊(duì)缴饭。然而,由于本地隊(duì)列有一個(gè)FIFO實(shí)現(xiàn)骆莹,未阻塞的goroutine不能保證馬上得到運(yùn)行,如果線程被其他goroutine占用担猛。下面是一個(gè)關(guān)于一個(gè)之前被channel阻塞但現(xiàn)在可執(zhí)行的goroutine例子:



G9在被channel阻塞后恢復(fù)幕垦。但是,它必須等G2傅联、G5和G4才能執(zhí)行先改。在這個(gè)例子中,G5將占用線程導(dǎo)致G9延遲執(zhí)行蒸走,會(huì)導(dǎo)致G9被其他處理器竊取的風(fēng)險(xiǎn)仇奶。從Go 1.5開始,從通道中恢復(fù)的goroutine將優(yōu)先被執(zhí)行比驻,這主要?dú)w功于P的一個(gè)特殊屬性:



G9現(xiàn)在被標(biāo)記為下一個(gè)可執(zhí)行g(shù)oroutine该溯。這個(gè)新的優(yōu)先級(jí)允許goroutine從通道中恢復(fù)就馬上得到執(zhí)行岛抄。然后其他goroutine現(xiàn)在將有運(yùn)行時(shí)間。這一改變總體對(duì)Go標(biāo)準(zhǔn)庫(kù)產(chǎn)生積極影響狈茉,改善了一些包的性能夫椭。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市氯庆,隨后出現(xiàn)的幾起案子蹭秋,更是在濱河造成了極大的恐慌,老刑警劉巖堤撵,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仁讨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡实昨,警方通過查閱死者的電腦和手機(jī)洞豁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屠橄,“玉大人族跛,你說我怎么就攤上這事∪袂剑” “怎么了礁哄?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)溪北。 經(jīng)常有香客問我桐绒,道長(zhǎng),這世上最難降的妖魔是什么之拨? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任茉继,我火速辦了婚禮,結(jié)果婚禮上蚀乔,老公的妹妹穿的比我還像新娘烁竭。我一直安慰自己,他們只是感情好吉挣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布派撕。 她就那樣靜靜地躺著,像睡著了一般睬魂。 火紅的嫁衣襯著肌膚如雪终吼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天氯哮,我揣著相機(jī)與錄音际跪,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛姆打,可吹牛的內(nèi)容都是我干的良姆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼穴肘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼歇盼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起评抚,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤豹缀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后慨代,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邢笙,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年侍匙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了氮惯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡想暗,死狀恐怖妇汗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情说莫,我是刑警寧澤杨箭,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站储狭,受9級(jí)特大地震影響互婿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辽狈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一慈参、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧刮萌,春花似錦驮配、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至元扔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旋膳,已是汗流浹背澎语。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人擅羞。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓尸变,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親减俏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子召烂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容