1. 程式人生 > >【冬瓜哥熊文】大話流水線(1)~流水線基礎理論

【冬瓜哥熊文】大話流水線(1)~流水線基礎理論

本期開始,冬瓜哥將分三次為大家全面介紹流水線有關的知識。

        試想兩個人在接力搬東西,如果這兩個人的速度能保持完全一樣,那麼配合會非常完美,我左手拿東西傳到右手,你左手剛好空出來拿到我右手遞給你的東西。但是突然你感覺頭上癢的不行了,去撓了一下,這下好了,我就得暫停,等你撓完了再繼續。此時我多麼想你跟前有個籃子啊,這樣我就可以放在籃子裡,你愛撓哪撓哪,撓完了你自己從籃子裡拿走。

對啊,程式設計師也是這麼想的。兩個裝置之間、兩個程式之間,要想達到高吞吐量,就得這樣將資訊的傳遞非同步化,而不是同步化。

        傳遞東西有兩種方式:

        A.同步等停方式。源頭某角色生成一樣物品,然後讓第一個人用左手從源頭拿走,傳遞給他的右手,傳遞給第二個人的左手,東西傳給第二個人之後,第一個人不做任何動作,純等待源頭再交給他另一樣物品。

想象一下,如果整個傳遞路徑上只有兩個人還算好,如果有10個人,可以想象,這樣物品從源頭傳遞到目的端所需要的時間將會非常長,而在這段時間內,源頭不會再傳遞任何物品,這10個人中總有9個人閒著沒事。一樣物品從第一個人傳遞到最後一個人所經歷的時間,被稱為這條傳遞鏈的時延/延遲。假設每個人從拿到物品到傳遞給下一個人,需要1ms的時間,那麼由10個人組成的傳遞鏈,整個傳遞鏈從頭傳遞一次就需要10ms的時間(該傳遞鏈條的時延=10ms),那麼這條傳遞鏈每秒可以傳遞1000ms/10ms=100個物品,也就是100物品/s,這就是該傳遞鏈的吞吐量

問一下:同步模式下,如何增加傳遞鏈的吞吐量?答案似乎只有一個:降低傳遞鏈的總時延。如何降低呢?要麼提高每個人的處理速度,要麼砍掉不必要的人。

人們天然的會想到,能否讓源頭源源不斷的將物品傳遞個第一個人,第一個人也源源不斷的傳遞給第二個人,以此類推。於是有了非同步方式。

        B.非同步流水線方式。源頭讓第一個人左手拿走一個物品,第一個人把物品傳遞到自己右手後,馬上再從源頭拿一樣物品。一瞬間,第一個人左手和右手同時拿著兩個物品。當第二個人結果第一個物品後,第一個人左手再將第二個物品傳遞給右手,左手空出,可以再從源頭拿第三個物品。第二個人向第三個人傳遞時也這樣去做。這就是非同步傳遞模式。問一下:這種傳遞模式下,一樣物品從源頭到目的,經歷了多長時間?當然還是10ms,沒的說,也就是說,傳遞鏈總時延並沒有變化,每樣物品從源頭傳遞到目的依然還是10ms。答對了,加十分。

問一下:此時該傳遞鏈每秒能傳遞多少樣物品?這問題得分析一下,第一樣物品從源頭傳遞到目的當然需要10ms,但是第二樣物品在第一樣物品到達之後的1ms(最後一個人從左手傳到右手的時間)也到達了,同理,後續所有的物品都是相隔1ms間距,一個接一個的到達了。那麼就可以算出來在1000ms內,頭10ms傳遞了一樣物品,後990ms每1ms可以傳遞一樣物品,這樣的話,吞吐量變為990+1=991物品/s。吞吐量幾乎提升了10倍!

問一下:在這個基礎上,想進一步提升吞吐量,共有幾種方式?自然,第一種方式是降低每個人從左手傳遞到右手的時間;這樣最見效,比如降低到0.5ms,則吞吐量將為:1+(1000-5)/0.5=1991物品/s。第二種則是減少傳遞鏈上的人的數量,這樣可以將第一個物品所需的10ms降低,比如,降低到2個人,那麼吞吐量將為1+(1000-2)/1=998物品/s,似乎提升並不是很大。第三種則是再增加一條或者多條傳遞鏈,多條一起傳遞,那直接效能翻對應的倍數。答對了,加十分。

問一下:是不是可以說,在非同步傳遞模式下,傳遞鏈的總體時延對吞吐量影響並不大?是的。答對了加五分。

問一下:既然傳遞每樣物品需要10ms,那麼每秒能傳遞1000ms/10ms=100樣物品啊,這好像是小學數學應用題中最簡單的那一道啊,為什麼上面算出來的卻是991樣/s呢?哪裡出了問題?仔細想來,還真有點燒腦。其實這裡的關鍵點在於,同一時刻內,有多個物品在同時並行向前傳遞,傳遞鏈中有幾個人在接力,傳遞持續穩定之後,同一時刻就有幾樣物品在傳遞。所以,上述例子中,併發度為10,忽略第一個物品傳遞時一段時間內並行度沒有達到10,所以最終吞吐量的確是100×10=1000物品/s。準確來講應該是:吞吐量=並行度×1000ms/節點時延。神奇啊!

問一下:如果傳遞鏈上的每個人都是各色人等,其各自從左手傳遞到右手的時間都不同,有快有慢。最終吞吐量是怎麼個情況?這可真有點難以用腦子想清楚,得畫一下,算一下才行。

640?wx_fmt=png

        先看看上圖左邊的情況,傳遞鏈兩頭是倆壯漢,中間夾了一位老爺爺和一位小朋友。很顯然,當第一個物品傳遞到目的之後,第二個物品需要等待40個時間單位才能到達目的,這是不是說明,後續每個物品都會以40個時間單位為間隔陸續到達呢?可以明確推出,是的。那麼是不是可以有這樣一個結論:後續每個物品的到達間隔統一為傳遞鏈中耗時最長的那個鏈條節點所耗費的時間呢?為了進一步證明該問題,我們把傳遞鏈右側的壯漢換成一位老奶奶,則的確,第二個物品會在第一個物品之後的50個時間單位到達,後續其他物品也都會以相隔50個時間單位的間隔到達。

        結論已經非常明確,可以明顯看到,只要傳遞鏈中有處理比較慢的節點,其他節點的處理速度再快也是沒用的,處理完了也只能原地等待。也就是說,即使把上面兩條傳遞鏈裡每個人分別都換成老爺爺/老奶奶,最終得到的吞吐量也是一樣的。

問一下:假設傳遞鏈中有一個節點時延為40個時間單位,其他所有節點都為10個時間單位,那麼如果將產生40個時間單位時延的這個人的位置上原地替換為3個10ms時延的人,吞吐量會不會有改善?當然有改善,吞吐量會提升40/10=4倍。這有點神奇了,人多了,吞吐量反而上來了。

問一下:有一條由10個人組成的傳遞鏈,每個人的處理時延是10ms。現在,將其更換為由20個人組成的傳遞鏈,而每個人的處理時延為5ms,吞吐量如何變化?根據上文中的結論,可以推斷出,吞吐量提升10/5=2倍,同時總時延不變。

問一下:替換為40個人,每人處理時延依然為5ms,相對20人每人5ms的傳遞鏈,吞吐量如何變化?可以看到,除了第一個物品會以200ms的時間傳過來之外,後續物品依然是以5ms為間隔到達,吞吐量與20人每人5ms的傳遞鏈保持一致。

非同步模式吞吐量公式:吞吐量=1/max[每個節點的時延]

同步模式吞吐量公式:吞吐量=1/sum[所有節點的時延]

同步/非同步模式時延公式:總時延=sum[所有節點的時延]

        可以看到,只要將某個物品的全部處理流程細分為若干個小流程,讓每一步小流程很快的完成,這樣就可以組成一條擁有極高吞吐量的傳遞鏈了。

         流水線,就是指上述的非同步傳遞過程。只不過傳遞鏈上的每個角色需要對物品做對應的處理而不是單純的傳遞。比如第一個人負責把物品做某種裝飾,第二個人負責對物品進行蓋章,第三個人負責用一張大包裝紙對物品進行包裝。這就是一條產品加工流水線。整個流水線中工序數量被稱為流水線的級數,本例這條產品包裝流水線為3級流水線。

        可以想象,第一步是最耗時的,需要在多個地方貼上對應的裝飾品,假設需要10秒鐘,第二步只是蓋個章,假設需要1秒鐘,第三步需要包起來,假設需要3秒鐘。很顯然,要讓這條流水線的產量提升的話,必須將第一步分解成3步,每一步只負責貼部分裝飾,假設耗時3秒,這樣就可以與第三步的時延匹配起來。能否繼續優化?也就是將每一步的時延降低為1秒鐘?第一步可以,用10個人,每個人只耗費1秒鐘貼一個裝飾品即可。但是最後一步恐怕不能再細分了,除非用機器,因為靠人工的話,包裝過程不可能被細分為比如第一步只折一個角,第二步再折另外一個角,因為當傳遞給下一步時,上一步折的角很可能已經自動鬆開了。這就是現實的無奈,最後一步會成為瓶頸。

        解決這個問題的辦法,就是找三個人來並行完成最後一步,也就是讓每人都處理一個物品,雖然每個人依然用3秒鐘包裝一個物品,但是3秒鐘內卻可以同時包裝3個物品,這樣的話就等價於每一步時延都為1秒時的吞吐量了。上述方式是物理上的可直觀感知的併發,而且真的是多個物品齊頭並進,可以稱之為多路物理併發模式;而多個人組成非同步傳遞鏈產生的併發傳遞,也是併發,只不過理解起來困難一些,可稱之為多級流水線併發模式,並且多個物品之間並非齊頭並進,而是有一定先後,相隔的時間就是max[每個節點的時延]。

640?wx_fmt=png

        使用4級流水線併發和4路物理併發獲取的吞吐量是相同的,只是方式有區別,前者是每10s出一個(每40s出4個),後者是每40s出4個。物理併發方式下,當第一輪傳遞開始之後的40s,會有4個物品傳出;而4級流水線併發模式下,傳遞開始後40s卻只有1個物品被傳出。

640?wx_fmt=png        從該圖可以看出,多路物理併發模式的起跑天然比流水線模式要快,但是跑起來之後,兩者的速度是相同的,實際中可以忽略這個起跑差異,畢竟這並不是比賽。我們把第一個物品從流水線進入到傳出的過程叫做入流水階段,此過程中,流水線會被充滿,一旦充滿,流水線就可以全速執行,也就是全並行階段。正是因為流水線必須先被充滿之後才能全並行,所以導致了其比物理併發模式起跑慢。

        另外,這兩個模式之間還有一個微妙的事情。假設該步驟的下游還有其他步驟的話,除非下游的步驟也是物理併發的,否則上一步的物理併發產生的起跑超前效應將會在下游步驟被遮蔽掉。如下圖所示,一股腦先到達了目的,倒頭來還得一個一個的經過下游步驟的處理,最終物理併發模式和多級流水線併發模式產生的吞吐量依然相同,同時每個物品到達時刻點也完全相同了。這好像龜兔賽跑,烏龜雖然一步一步往前挪,但是最後反而和兔子一起到達了終點。

640?wx_fmt=png

提示

誰在乎這“可以忽略”的起跑超前效應?比如有一類場景是金融領域的高頻交易,交易者必須在股票或者某種金融產品上漲或者下跌一定數額(通常是極微量的增幅)之後立即發起並完成交易,因為有大量的交易者在排隊,誰先第一時間從證券交易所伺服器上獲知對應的漲幅並且將交易請求儘快傳送到交易伺服器,誰的交易請求就能排在佇列前面,就可以搶先賣出或者買入。而交易請求,就像是一樣物品,會在多個角色之間傳遞,有一條傳遞鏈存在。但是這類交易往往是同步模式居多,也就是傳送一條訊息,等待對方伺服器返回確認訊息之後,再發送下一條訊息。

問一下:對於下游步驟沒有物理併發的場景,如果將上游步驟的物理併發度提升一下,比如從4提升到8,會不會有收益?見下圖,很顯然,沒有任何作用。

640?wx_fmt=png

問一下:如果在下游非併發步驟之後再增加一個併發步驟,會不會有什麼收益?根據下圖,很顯然,沒有任何收益。

640?wx_fmt=png

問一下,如果上述例子中將上面傳遞鏈的第一級時延降低到與第二條傳遞鏈第一級時延相同,會不會有區別?根據下圖所示,吞吐量沒有區別。

640?wx_fmt=png

問一下:兩條傳遞鏈,級數相同,每一級時延相同,但是第一條傳遞鏈上第一級4路併發,第二級2路併發,第三級4路併發。這兩條傳遞鏈的吞吐量相比有什麼差異?根據下圖可判斷,第一條傳遞鏈的吞吐量為第二條的2倍而不是4倍。也就是說,併發度最小的那一級決定了整個傳遞鏈的物理併發度。

640?wx_fmt=png        根據對上幾問的分析,最終可以有這個結論:非同步傳遞鏈上每一級的吞吐量公式為:

  • 當上級時延=下級時延時,吞吐量=min[物理併發度]/時延

  • 當上級時延<下級時延時:

  • 當(下級併發度/上級併發度)≤(下級時延/上級時延)時,吞吐量=下級併發度/下級時延

  • 當(下級併發度/上級併發度)>(下級時延/上級時延)時,吞吐量=上級併發度/上級時延

  • 當上級時延>下一級時延時:

  • 當上級併發度≤下級併發度時,吞吐量=上級併發度/上級時延

  • 當(上級併發度/下級併發度)<(上級時延/下級時延)時,吞吐量=上級併發度/上級時延

  • 當(上級併發度/下級併發度)>(上級時延/下級時延)時,吞吐量=下級併發度/下級時延

  • 當(上級併發度/下級併發度)=(上級時延/下級時延)時,吞吐量=上級併發度/上級時延或者下級併發度/下級時延

非同步傳遞鏈的總吞吐量公式為:吞吐量=min[每一級的吞吐量]。

640?wx_fmt=png