以一份簡潔的分數測試報告來說明時脈不代表效能,真正的效率取決於「在一個 clock cycle 中能做多少事情」。
將 CPU 處理資料的部份分為數個 stage,藉由這種設計可以讓處理的效率更好,因為每個 stage 在任意時間都有在工作。
而各個 stage 間是利用 latch 來傳遞資料給下一個 stage。
除此之外,因為資料的相依性,所以會從 Execute 或 Writeback 之後拉一條線到 Execute 之前,這樣可以減少不必要的等待。
再來就是,Execute 階段中其實是由好幾組邏輯運算單元組成。
RISC 相較複雜指令集較容易實現 pipeline,也因此,一個較低時脈的 RISC 處理器,可能會較時脈高但指令集較複雜的處理器效能好。
如同上一部份所說,Execute 中有好幾組邏輯運算單元,一條指令中不一定都會用到全部的運算單元,所以我們可以多讀取指令,依照所需分配到不同路徑。
利用上面 pipeline 的圖案呈現出來就像這樣
概念呈現影片:
VLIW 也就是 Very Long Instruction Word。表示一條指令中可以執行多種操作。
好處是不用檢查資料相依性。壞處是遇到 cache miss 的話,處理器就要在那邊空等。
既然 pipeline 可以讓 CPU 使用地更有效率,那我們只要把 stage 跟的更細,效能就會變得更好?從文章中得到的答案是否定的。
較深的 pipeline 的 latency 會較高,也因此不會比較短的 pipeline 效率較高,因為 instruction dependency 導致 pipeline 階段中不得不填上 bubble。memory load 的 latency 也是十分麻煩,因為通常在程式一開始就會載入記憶體,此時僅有較少的指令可以填入空檔,而且 load latency 也取決於資料是否存在 cache。
在文中的例子,Line 2 必須等待 Line 1 運算的結果,如果 stage 很多,就表示所需要等待的時間可能會更長。
而當一個指令從 Execute 階段到運算的資料能夠被其他指令使用,其中所經過的時間就叫 Instruction Latency。
當遇到 branch instruction 時,CPU 不知道到底要抓下一個指令,還是跳躍後的那一個指令,所以 CPU 只能去預測。
預測的方法分為兩種,第一種是靜態的預測,也就交給 compiler 來做個 mark。第二種則是由 CPU 來根據過往的紀錄來進行預測。
文中也以 Pentium Pro/II/III 舉例,再怎麼聰明的 predictor 也只有 90% 的正確率,而預測失敗時會損失 30% 的效能。換句話說,Pentium Pro/II/III 有三分之一的時間根本沒做到什麼事,只在那邊說「糟糕~ 錯了」。
為了避免 branch instruction,所以有些平台會提供 conditional move instruction。
這一個區段主要的概念就是重新排序指令避免各種 hazard 跟 latency。
根據重新排序的方式可以分為靜態與動態。
The Power Wall
當增加 20% 的 clock speed,功耗就增加更多,如 50%。
為了提升 clock speed 而必須要提供更多的電力以及改善散熱能力,並不那麼簡單,所以在這邊就遇到了一個瓶頸,稱之為 The Power Wall。
The ILP Wall
因為程式中的 load latency、cache miss、branch prediction 等問題,讓程式沒辦法有效的平行化,導致所謂的 The ILP Wall。
在現今的 x86 架構中,會把原先的 x86 指令轉換成類似 RISC 的指令才進行處理。那些類 RISC 的指令又稱為 micro ops (也寫作 μops)
SMT 的處理器核通常會比較大一點,在相同大小的情況下,可以放入較多的普通處理器核。
那哪一種會比較好?端看程式的用途。
為了從記憶體當中載入資料,會花費許多時間,導致效能受到限制,這種情況就叫 The Memory Wall。
所以在現今的設計中都會使用 cache 來進行快取。
由於中央處理器的效能進展迅速,記憶體經常會有跟不上的情形。為提昇中央處理器的效率,從 i486 開始,所有 Intel 設計的中央處理器都有內建一定大小的 cache memory 來增進記憶體讀取的效率。
cache 的原理是利用一小塊高速記憶體來存放最近使用過的的程式碼或資料,如此一來,這些資料在重覆使用時就不必每次都到系統記憶體上拿。由於程式經常會重覆存取在同一區域內的記憶體資料 (及 locality),因此 cache 對系統效率的提昇有相當大的幫助。
換言之,cache 是個緊鄰處理器,速度快但容量小的記憶體。因為容量小,一定不可能把需要的資料都快取起來,所以採用階層式的概念來快取資料。
如 L1 cache, L2 cache, L3 cache,速度由快到慢,大小由小到大。
這樣聽起來好像很有道理,但是其中有一個小問題:雖然個人電腦的架構在設計上是把記憶體位址和輸出入位址分開,但是在很多情況下我們仍有記憶體對映輸出入 (memory-mapped IO; MMIO) 的需求,例如螢幕顯示的控制,以標準的 VGA 卡來說, 當我們在對 0A0000h 到 0BFFFFh 這一段記憶體進行操作時,實際上資料是送到顯示卡去,而不是到系統記憶體上。為了維持這類記憶體對映輸出入的正常運作,這些記憶體位址是不能夠被 cache 處理的。那麼中央處理器怎麼知道哪些記憶體位址可以快取而哪些不行呢?
這個問題通常是由軟體和硬體一起合作解決的;中央處理器對軟體控制 cache 的能力都有不同的規範。
另外,根據不同的設計,可以分為 direct-mapped cache、N-way set associative cache。
以下改寫自 淺談 memory cache
當初記憶體為什麼要像現在設計成階層式的架構?很簡單,因為我們發現我們寫的程式在存取記憶體的過程中,有兩大現象:
這就產生了設計記憶體架構的兩大原則,Temporal Locality 和 Spatial Locality。另外還有記憶體本身的速度、成本的考量,越快的記憶體越貴,我們決定設計出階層式的記憶體架構。
越上層的記憶體越貴,速度越快,容量越小。越下層記憶體越便宜,速度慢,但容量越大。資料只會在相鄰兩層間移動。
我們把資料一次從記憶體下層轉移到記體上層的單位定作 block。若處理器要求讀取某個 block 的資料,剛好在上層的記憶體內,那就稱為 hit。反之,如果不在上層,那就稱為 miss。hit rate 就是你成功在上層記憶體就找到你要的資料的次數比例,相反就是 miss rate,當然 hit rate + miss rate = 1。現今電腦的 hit rate 可達到驚人的 95% 以上。
另一組對電腦效能來說影響重大的因素:
cache 在設計時,要先回答一個重要的問題:
處理器怎麼知道資料是否在 cache 中,並正確從 cache 抓出想要的資料?