深刻淺出計算機組成原理學習筆記:第二十三講

1、引子

一、解決不一樣指令之間的數據依賴問題。

上一講,我爲你講解告終構冒險和數據冒險,以及應對這兩種冒險的兩個解決方案。一種方案是增長資源,經過添加指令緩存和數據緩存,讓咱們對於指令和數據的訪問能夠同時進行。
這個辦法幫助CPU解決了取指令和訪問數據之間的資源衝突。另外一種方案是直接進行等待。經過插入NOP這樣的無效指令,等待以前的指令完成。這樣咱們就能解決不一樣指令之間的數據依賴問題java

二、上一講的這兩種方案這兩種方案都有點兒笨。

着急的人,看完上一講的這兩種方案,可能已經要跳起來問了:「這也能算解決方案麼?」的確,這兩種方案都有點兒笨。緩存

第一種解決方案,比如是在軟件開發的過程當中,發現效率不夠,因而研發負責人說:「咱們須要雙倍的人手和研發資源。」而第二種解決方案,比如你在提需求的時候,研發負責人告訴你說:「來不及作,你只能等
咱們需求排期。」 你應該很清楚地知道,「堆資源」和「等排期」這樣的解決方案,並不會真的提升咱們的效率,只是避免衝突的無奈之舉。學習

那針對流水線冒險的問題,咱們有沒有更高級或者更高效的解決方案呢?既不用簡單花錢加硬件電路這樣「堆資源」,也不是純粹等待以前的任務完成這樣「等排期」spa

答案固然是有的。這一講,咱們就來看看計算機組成原理中,一個更加精巧的解決方案,操做數前推翻譯

2、NOP操做和指令對齊


要想理解操做數前推技術,咱們先來回顧一下,第5講講過的,MIPS體系結構下的R、I、J三類指令,以及第20講裏的五級流水線「取指令(IF)-指令譯碼(ID)-指令執行(EX)-內存訪問(MEM)-數據寫回
(WB) 」。我把對應的圖片放進來了,你能夠看一下。若是印象不深,建議你先回到這兩節去複習一下,再來看今天的內容3d

在MIPS的體系結構下,不一樣類型的指令,會在流水線的不一樣階段進行不一樣的操做。blog

咱們以MIPS的LOAD,這樣從內存裏讀取數據到寄存器的指令爲例,來仔細看看,它須要經歷的5個完整的流水線。STORE這樣從寄存器往內存裏寫數據的指令,不須要有寫回寄存器的操做,
也就是沒有數據寫回的流水線階段。至於像ADD和SUB這樣的加減法指令,全部操做都在寄存器完成,因此沒有實際的內存訪問(MEM)操做。
事件

 

 


有些指令沒有對應的流水線階段,可是咱們並不能跳過對應的階段直接執行下一階段。否則,若是咱們前後執行一條LOAD指令和一條ADD指令,就會發生LOAD指令的WB階段和ADD指令的WB階段,
在同一個時鐘週期發生。這樣,至關於觸發了一個結構冒險事件,產生了資源競爭。
圖片

因此,在實踐當中,各個指令不須要的階段,並不會直接跳過,而是會運行一次NOP操做。經過插入一個NOP操做,咱們可使後一條指令的每個Stage,必定不和前一條指令的同Stage在一個時鐘週期執行。
這樣,就不會發生前後兩個指令,在同一時鐘週期競爭相同的資源,產生結構冒險了。內存

 

 

3、流水線裏的接力賽:操做數前推

經過NOP操做進行對齊,咱們在流水線裏,就不會遇到資源競爭產生的結構冒險問題了。除了能夠解決結構冒險以外,這個NOP操做,也是咱們以前講的流水線停頓插入的對應操做。
可是,插入過多的NOP操做,意味着咱們的CPU老是在空轉,幹吃飯不幹活。那麼,咱們有沒有什麼辦法,儘可能少插入一些NOP操做呢?不要着急,
下面咱們就以兩條前後發生的ADD指令做爲例子,看看能不能找到一些好的解決方案。

add $t0, $s2,$s1
add $s2, $s1,$t0

這兩條指令很簡單。
1. 第一條指令,把 s1 和 s2 寄存器裏面的數據相加,存入到 t0 這個寄存器裏面。
2. 第二條指令,把 s1 和 t0 寄存器裏面的數據相加,存入到 s2 這個寄存器裏面。

由於後一條的 add 指令,依賴寄存器 t0 裏的值。而 t0 裏面的值,又來自於前一條指令的計算結果。因此後一條指令,須要等待前一條指令的數據寫回階段完成以後,才能執行。
就像上一講裏講的那樣,咱們遇到了一個數據依賴類型的冒險。因而,咱們就不得不經過流水線停頓來解決這個冒險問題。咱們要在第二條指令的譯碼階段以後,插入對應的NOP指令,
直到前一天指令的數據寫回完成以後,才能繼續執行。這樣的方案,雖然解決了數據冒險的問題,可是也浪費了兩個時鐘週期。咱們的第2條指令,其實就是多花了2個時鐘週期,運行了兩次空轉的NOP操做。

 

 

不過,其實咱們第二條指令的執行,未必要等待第一條指令寫回完成,才能進行。若是咱們第一條指令的執行結果,可以直接傳輸給第二條指令的執行階段,做爲輸入,那咱們的第二條指令,
就不用再從寄存器裏面,把數據再單獨讀出來一次,纔來執行代碼。

咱們徹底能夠在第一條指令的執行階段完成以後,直接將結果數據傳輸給到下一條指令的ALU。而後,下一條指令不須要再插入兩個NOP階段,就能夠繼續正常走到執行階段。

 

 

這樣的解決方案,咱們就叫做 操做數前推(Operand Forwarding),或者操做數旁路(OperandBypassing)。其實我以爲,更合適的名字應該叫 操做數轉發。這裏的Forward,
其實就是咱們寫Email時的「轉發」(Forward)的意思。不過現有的經典教材的中文翻譯通常都叫「前推」,咱們也就不去糾正這的「轉發」(Forward)的意思。不過現有的經典教材的中文翻譯通常都叫「前推」,咱們也就不去糾正這個說法了,你明白這個意思就好。

轉發,實際上是這個技術的 邏輯含義,也就是在第1條指令的執行結果,直接「轉發」給了第2條指令的ALU做爲輸入。另一個名字,旁路(Bypassing),則是這個技術的 硬件含義。爲了可以實現這裏的「轉發」,
咱們在CPU的硬件裏面,須要再單獨拉一根信號傳輸的線路出來,使得ALU的計算結果,可以從新回到ALU的輸入裏來。這樣的一條線路,就是咱們的「旁路」。它越過(Bypass)了寫入寄存器,再從寄存器讀出的過程,也爲咱們節省了2個時鐘週期。

操做數前推的解決方案不但能夠單獨使用,還能夠和流水線冒泡一塊兒使用。有的時候,雖然咱們能夠把操做數轉發到下一條指令,可是下一條指令仍然須要停頓一個時鐘週期。好比說,咱們先去執行一條LOAD指令,再去執行ADD指令。LOAD指令在訪存階段才能把數據讀取出來,因此下一條指令的執行階段,須要在訪存階段完成以後,才能進行。

總的來講,操做數前推的解決方案,比流水線停頓更進了一步。

流水線停頓的方案,有點兒像游泳比賽的接力方式。下一名運動員,須要在前一個運動員遊玩了全程以後,觸碰到了游泳池壁才能出發。
而操做數前推,就好像短跑接力賽。後一個運動員能夠提早搶跑,而前一個運動員會多跑一段主動把交接棒傳遞給他。

4、總結延伸

這一講,我給你介紹了一個更加高級,也更加複雜的解決數據冒險問題方案,就是操做數前推,或者叫操做數旁路。

操做數前推,就是經過在硬件層面製造一條旁路,讓一條指令的計算結果,能夠直接傳輸給下一條指令,而再也不須要「指令1寫回寄存器,指令2再讀取寄存器「這樣畫蛇添足的操做。
這樣直接傳輸帶來的好處就是,後面的指令能夠減小,甚至消除本來須要經過流水線停頓,才能解決的數據冒險問題。

這個前推的解決方案,不只能夠單獨使用,還能夠和前面講解過的流水線冒泡結合在一塊兒使用。由於有些時候,咱們的操做數前推並不能減小全部「冒泡」,只能去掉其中的一部分。
咱們仍然須要經過插入一些「氣泡」來解決冒險問題。

經過操做數前推,咱們進一步提高了CPU的運行效率。那麼,咱們是否是還能找到別的辦法,進一步地減小浪費呢?畢竟,看到如今,咱們仍然少不了要插入不少NOP的「氣泡」。那就請你繼續堅持學習下去。下一講,咱們來看看,CPU是怎麼經過亂序執行,進一步減小「氣泡」的。

相關文章
相關標籤/搜索