趣談Java高併發(一)併發的硬件相關(一):指令流水

一、什麼是指令流水

1、首先了解一下什麼是併發,什麼是並行。

  1. 所謂的並行,同時包含同時性和併發性。同時性說的是在同一個時刻有一個以上的時間發生,而併發性是指有一個以上的任務在同一時間段發生。也就是說並行 = 併發 + 同一時刻發生。
  2. 並行性有四個級別:作業/程序級、任務/進程級、指令之間級、指令之內級。前兩級使用算法進行調度實現,也就是程序、線程調度,要實現這兩個級別的並行至少需要兩個CPU內核,一個內核的時候是併發。後面兩個級別需要硬件進行支持,其中指令之間並行也是需要多個內核的,但是指令之內級的並行只需要一個內核。指令流水就是後兩個級別的並行,且主要是指令內的並行。

2、什麼是流水線

有這麼一段話:

大家好,我是來自富土康3號流水線的工人,張全蛋。…

在印象中,流水線什麼的都是製造業纔有的東西,《國富論》提出:勞動分工是提高效率的關鍵。流水線就是勞動分工的產物,流水線根據產品生產的不同階段進行生產分工,每個生產段負責某個具體環節,從而提高生產效率。

將流水線的思想用到指令執行上,就是指令流水。

指令流水的原理很簡單,利用指令完成需要多個階段的特點,將一條指令的多個階段進行流水線式的執行,以提高CPU各個組件的利用率。

先來一個不正經的比方,假如我們一羣人要生產一個洋娃娃,但是甲只會安裝頭,乙只會安裝身體,丙只會給洋娃娃穿衣服。那我們生產洋娃娃的時候,爲了追求最大的產量肯定是這個過程:

  1. 甲安好頭後給乙安裝身體,開始下一個洋娃娃的頭部安裝。
  2. 乙接過甲只有頭的洋娃娃後給她安裝上身體,然後把她交給丙,繼續安裝甲遞過來的下一個只有頭的洋娃娃
  3. 丙接過乙沒有衣服的洋娃娃後給她穿上衣服,然後把她放在產品櫃裏面,繼續給乙遞過來的沒有衣服的洋娃娃穿衣服
  4. 如此循環往復…

再來一個例子,現在我來模擬一段程序,就是飯店炒菜上菜的場景。因爲CPU不是一個單獨的組件,而是一羣組件,所以我們用一個後廚團隊來模擬CPU,用前臺下的菜單來模擬一條條指令。在這個廚師團隊中,肯定是有一個老大(CU)的,就是廚師長,他負責協調團隊。還有一個專門跑腿拿食材的甲,一個專門處理食材的乙,一個專門抄菜的丙,一個專門裝盤送菜的丁,現在開始場景模擬:

  1. 前臺:一號桌宮保雞丁一份、土豆肉絲一份、麻辣魚一份,拔絲日本豆腐一份…
  2. 老大:甲,去拿一份宮保雞丁的材料。
  3. …:甲跑去拿宮保雞丁的材料,乙丙丁這個時候是閒着的。
  4. 老大:乙把這宮保雞丁的材料處理了,甲去拿土豆肉絲的材料。
  5. …:甲跑去拿土豆肉絲的材料,乙處理宮保雞丁的材料,丙丁這個時候是閒着的。
  6. 老大:丙把宮保雞丁炒了,乙把土豆肉絲的材料處理了,甲去拿麻辣魚的材料。
  7. …:甲去拿麻辣魚的材料,乙處理土豆肉絲的材料,丙炒着宮保雞丁,丁還閒着的。
  8. 老大:丁把宮保雞丁裝盤送出去,丙把土豆肉絲炒了,乙把麻辣魚的材料處理了,甲去拿拔絲日本豆腐的材料來。
  9. …:甲去拿拔絲日本豆腐的材料,乙在處理麻辣魚的材料,丙在炒土豆肉絲,丁把宮保雞丁裝盤送給了客人。
  10. 如此循環往復,直到飯店打烊…

最後來一個正經的解釋,假設每一條計算機指令的執行都需要經歷如下六個步驟:

  1. 取址:FI;
  2. 指令譯碼:DI;
  3. 計算操作數地址:CO;
  4. 取操作數:FO;
  5. 執行指令:EI;
  6. 寫操作數:WO;

那麼指令流水線就相當於下圖這樣的一個流程:
指令流水圖示

經過上面三個例子,應該可以感受什麼是指令流水了,接下來討論爲什麼要這樣子做?

二、爲什麼要指令流水?

仔細看看我剛剛寫的三個例子,裏面都有兩個特點:

  1. 所有的組件或者說人員,在除開第一個指令開始的時候,其他任何時候所有的人員都是在運行忙碌的,就飯店那個例子就是到最後發現沒有人是閒着的了。
  2. 所有的任務都被劃分爲時間相同的步驟,每一個步驟都能夠同時完成。

就第一個特點,可以看出來:指令流水線相較於一個指令完成後再開始下一個指令的情況,就理論執行效率上來說,一條指令能劃分爲幾個流水步驟,處理速率就能夠達到幾倍!!這是非常的的效率提升,所以指令流水是非常有必要的。

三、指令流水的限制條件

指令流水線可以提高效率,但是現實並沒有理論那麼美好,有幾個方面的原因造成不可能出現理論上的效率:

  1. 結構相關:重疊執行的指令對同一資源競爭而產生衝突,例如訪存衝突(一條指令取數據,一條指令取地址,但是地址與數據在同一個存儲器,該存儲器只有一個訪問接口,這個時候就需要暫停後面一個指令的執行以解決衝突)
  2. 數據相關:重疊執行的指令有可能會改變對操作數的寫訪順序,從而導致數據相關衝突。例如後一條指令需要前一條指令的執行結果,但是當後一條指令需要讀取前一條指令的結果時,前一條指令還沒有將結果寫回去。這個時候需要暫停後面的指令。常見的數據相關有寫後讀相關(下一條指令讀到舊值)、讀後寫(上一條指令讀到新值)、寫後寫(下一條指令的修改值被覆蓋)。
  3. 控制相關:轉移指令更改PC值,導致後面的指令流水全部失效,直接斷流。
  4. 指令流:程序指令是我們編碼的,不瞭解底層原理,所寫的代碼指令無法構成指令流水,而且一條指令多個步驟的執行時間不一致,會卡流。

四、優化指令流水

指令流水的效率提升簡直讓人垂涎欲滴,既然提出這個理想狀況,那麼就要克服困難去接近那個目標,前面三個限制條件只有硬件支持,最後面一個就看起來可以搞點文章,這就是指令重排。

指令重排:

在不影響as-if-serial語義的情況下,對程序指令進行亂序執行。

就是這個萬惡的優化帶來了一系列問題,但是比起帶來的性能提升,也就忍了。

下一篇博文我將分享指令重排的見解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章