1. 程式人生 > >計算機體系結構小總結

計算機體系結構小總結

    最近,我閱讀了一篇介紹計算機體系結構的文章,感覺比較簡潔而且全面的介紹了計算機體系結構的內容。文章的連結是Modern Microprocessors A 90-Minute Guide!。因此,我謹以此文來做下總結,好讓以後來回憶。

    首先,我先說下對計算機體系結構(computer architecture)的理解。我覺得計算機體系結構就是計算機組成原理,而且與IC設計密切相關,核心的問題就是在既定的指令集下,如何設計CPU的微架構來使它儘可能的快速執行指令。還有一點就是,在摩爾定律的作用下,也就是製造工藝的越來越精細,在相同的芯片面積上,我們設計微架構可用的電晶體越來越多。因此,如何有效的利用這些數以億計的電晶體來為我們的目標服務,也是一個很重要的問題。 

    下面,我就總結一下內容。

  • 不只是頻率  

         一個處理器的效能與它的頻率有一定的關係,但是不一定頻率越高的處理器效能就越好。這就要涉及到在不同的微架構下,一個時鐘週期到底幹了什麼事。也就是要比較不同微架構的優越性了。

  • 流水線和指令級並行(ILP)

          一開始的處理器處理指令是一條接著一條的,這是簡單明瞭的,但是也是效率低下的。因為指令執行的過程中至少可以分為4個階段,分別為:取指令、解碼、執行和寫回儲存器。這幾個階段負責的模組只有在相應的階段才會處於工作狀態,其餘階段都是閒置,於是這顯然就有提升的空間。沒錯,就是讓下一條指令接著馬上執行。這就是流水線設計的核心思想。具體就是:當一條指令取完了,就可以立即取下一條指令了,後面的操作以此類推。

       

沒有流水線的處理器
沒有流水線設計的處理器

 

有流水線設計的處理器

       可以看到,流水線的設計使處理器的CPI(cycle per instruction)從4降到了1。當然這是理論值。其實,我覺得這已經是一種指令級並行的形式了,只不過這種方法屬於比較簡單的。還有,早期的RISC處理器d都實現了5階段的流水線設計。而早期的CISC處理器,如80386,因為複雜指令集的原因沒有實現流水線設計。

  • 更深的流水線——超流水線

       因為時鐘週期的長度是由最耗時的流水線階段來決定的,所以一種思想就是將那些耗時長的階段再分成一些較短的階段。這樣做的話就能使時鐘週期儘可能的短,從而提升了處理器的頻率。還有,因為CPI還是1,而每秒鐘的週期數提升了,所以總體的效能提升了。

 

超流水線設計的處理器
  • 多發射(Multiple Issue)——超標量 (Superscalar)

        因為在執行階段有許多的功能單元,比如整數處理單元、浮點數處理單元和訪存處理單元,它們各自完成它們自己的任務。於是,我們就會想到能不能讓它們同時工作起來而不閒置,從而提高效率。為了做到這樣,就必須改進取指令單元和解碼單元,讓它們能同時取和解碼多條指令,從而根據指令功能的不同分發給不同的處理單元。

   

超標量設計的處理器(3發射)

      鑑於不同的處理單元所要求的處理階段數量不同,於是每條流水線的深度就不同。於是,當我們描述一個處理器的流水線深度時通常是用它處理整數的深度,因為這個深度通常是最少的。

     當然,我們可以讓一個處理器同時是超標量和超流水線設計的,因為這兩種設計並不衝突。最終,在這種設計加持下,指令級並行程度進一步提高。

     

一個超標量和超流水線設計的處理器
  • 顯式並行——VLIW(Very Long Instruction Word) 

         這種設計是重新設計指令集,使每條指令包含多條小指令,從而變得很長(very long instruction)。這樣做的話就相當於一條指令就能讓多個功能單元都工作起來,與超標量處理器非常相似,但是大大簡化了解碼和分發單元的邏輯設計。但是我們程式設計的方式並沒有變,所以指令間的相關性還是存在。因此我覺得這種設計的實質是把指令間相關性檢查的工作從硬體轉移到編譯器(軟體) ,從而簡化了硬體的設計,讓處理器的速度得以提升。

 

一個VLIW設計的處理器

      但是,這種設計的處理器都沒有取得商業上的成功。其中最著名的例子就是Intel的IA-64架構,Intel希望用這個指令集取代X86指令集,但是最終被AMD的X86_64指令集打敗。

  • 指令相關性和延遲 

        既然超流水線和多發射這麼好,那為什麼不盡量讓這些指標多些呢?比如做一個50級流水線和20發射的處理器。。。

        但是,實際上不是這樣的,很多指令間都存在相關性,比如下一條指令要用到上一條指令的執行結果。這樣就阻止的指令的並行執行。還有,不同指令的執行時間不同,有的要流水線較短,有的較長,所以不同指令的執行延遲就不同。對於短延遲的整數操作,下一條指令可以馬上開始解碼執行,但是對於長延遲的浮點數操作,下一條相關指令就必須等待。這些相關性就限制了指令間的並行性。

  • 分支和分支預測 

        還有一個影響指令級並行的因素是分支指令。對於流水線設計的處理器,指令是一條接著一條馬上執行,但是如果出現了分支指令,就代表著緊接著的下面的指令有可能是不用執行了。若執行了,就是在做無用功。而且,對於越深的流水線,無用功就越多。對於這種情況,處理器可以選擇等待分支指令的執行結果,但是這會大大降低效能,因為平均下來,一個程式有1/6的指令是分支指令。因此,處理器必須猜一條執行路徑。對於猜測路徑,分為兩種方法。一種是編譯器來猜,這種方法稱為靜態分支預測。這種方法實際上是把工作重點放在軟體上,但是編譯器通常也很難預測分支的走向。另一種方法就是處理器自己猜,這種方法稱為動態分支預測。在這種處理器中,通常都有負責分支預測的模組。鑑於分支預測的重要性,目前處理器的分支預測模組非常複雜和龐大。目前最牛逼的預測準確率能達到95%。

  • 用斷言消除分支預測 

       這個小節說白了就是用條件轉移指令來消除分支預測。(不太懂。。。)

  •  指令排程,暫存器重新命名和亂序執行

        因為分支指令和長延遲指令會使流水線空閒一段時間。 一種思想就是讓這些空閒著的流水線執行在後面的不相關的指令。這就打亂了指令的順序執行性,於是把這種設計稱為亂序執行。對於亂序執行,有些指令雖然不相關,但是它們會用到相同的暫存器(因為暫存器就是那麼幾個),比如都往同一個暫存器寫內容,這樣也會限制了它們的並行執行。於是又提出來一種技術,稱為暫存器重新命名。我的理解就是處理器內部擁有許多隱藏的暫存器,為了讓這些用相同暫存器的指令同時執行,只能讓這些指令使用不同的隱藏的暫存器,而且又為了保證一致性,這些隱藏的暫存器就會被暫時重新命名為這條指令所指定的暫存器。

     對於如何實現亂序執行,與分支預測相似,也有兩種方法。一種是編譯器負責重排指令順序,提高處理器執行指令的並行度,稱為靜態指令排程。這種方法其實就是完全的軟體解決方案,處理器一點改變都沒有。另一種就是處理器負責亂序執行後面的指令,稱為動態指令排程。這種方法需要處理器的取指令和解碼/分發模組大大增強,增加了處理器設計的複雜性和處理器的功耗。  

      最終,這種技術又進一步的提高了處理器的指令級並行程度。

  • The Brainiac(能力超群的) Debate

       這個爭論說白了就是處理器核心應該選擇亂序執行設計還是順序執行設計。亂序執行設計的速度快但是核心面積大,導致單個晶片能整合的核心數減少。 順序執行設計的速度較慢但是核心面積小,於是單個晶片能整合的核心數增加。這就是一種權衡嘛!

  • 功耗牆和ILP(指令級並行)牆

       這個小節主要就說明了處理器核心的頻率不能無止境的提升,提升到一定程度就會使發熱和功耗暴增(呈指數形式的),這就是為什麼今天處理器的極限頻率都差不太多。(5GHz基本極限了) 這個現象稱為功耗牆。於是採用順序執行設計的處理器的頻率遇到了瓶頸,被採用亂序執行設計的處理器迎頭趕上,於是效能差距變大。

      當然,這也不是說明採用亂序執行設計就是完美的。因為真實軟體的指令流的可並行性實在是有限。當前,主流軟體的單執行緒模式下,指令級並行程度最多在大約2~3指令每時鐘週期,即2~3IPC。這種現象稱為ILP牆。

  • 關於X86指令集

      X86指令集是複雜指令集,它是很難運用上面所說的各種技術的。於是,Intel終於想出了一種辦法,就是在處理器內部新增一個 翻譯模組,把讀取到了X86指令翻譯為1~3條類似於RISC指令的微小操作指令μops,後面再根據這些微小指令來進行處理操作。其實,這些微小操作指令集基本就相當於一種RISC指令集了。於是,能順利用到RISC指令集上的技術都能順利的用到X86指令集世界了,這大大增強了X86這個古老的CISC指令集的競爭力。這也說明CISC和RISC之爭是有結論的,RISC取得勝利!

一個把X86指令解碼成類RISC指令來執行的處理器
  • 執行緒——SMT、超執行緒和多核 

       因為單個指令流的並行度有限,導致超標量、流水線設計的處理器的許多功能模組很多時候都是處於空閒狀態。於是,有種思想就是既然單個指令流(單個執行緒)的並行度有限,那麼我就在空閒的時候執行另一條執行緒的指令流不就能充分利用處理器的功能模組了嗎?因為這完全是兩個指令流,基本上它們是互不相干的,除非有同步機制,所以它們兩個之間的指令的並行度肯定很高。於是,基於這種思想,Intel推出了超執行緒技術,一個核心能同時執行兩個執行緒。那麼既然要同時執行兩個執行緒,那麼單獨記錄每個執行緒的執行狀態的單元必然要有兩份,這些單元包括程式計數器(PC)、體系結構可見的全部暫存器和快取頁表記錄的TLB等等。其實,這些單元只佔很少的芯片面積,真正的大頭:解碼器、功能單元和cache等等都是可以共用的,所以這個設計大約只增加10%的芯片面積 。

    還有,雖然一個核心能同時執行兩個執行緒,但是畢竟只是一個核心,那麼不同執行緒之間對於相同功能單元的利用必然要競爭,有競爭就必然有等待。所以速度肯定是不如兩個核心執行兩個執行緒的,還有可能造成兩個執行緒都執行得很慢。這就要靠處理器廠商的慢慢優化了,我這裡只說明大概思想。

   

一個採用SMT設計的處理器

    這種技術利用的並行性稱為執行緒級並行。 

  • 更多的核心還是更寬的核心? 

        本節就主要討論一個晶片的核心應該怎麼設計好,是應該每個核心把上面的技術都用上呢,還是儘量保持小核心呢?如果都把上面的技術用上,那麼單個核心的面積必然會很大,於是單個晶片能整合的核心數就變少。這種情況就相當於單執行緒的速度很快,但同時執行的執行緒數較少。如果儘量保持小核心,就是有選擇的使用上面所述的技術,那麼單個核心的面積就會變小,於是單個晶片能整合的核心數就會變多。這種情況就相當於單執行緒的速度變慢,但是同時執行的執行緒數增多。

      其實,這兩種設計沒有孰優孰劣,只是各自都有自己適合的應用領域。Intel的桌面處理器通常都是核心超大,就是單核能力超群,而單晶片的核心數較少。 

  •  資料級並行——SIMD 向量指令

        說完了指令級並行和執行緒級並行,現在就來說說另一種並行——資料並行。我覺得資料並行就是對一組資料都做相同的操作,於是我們可以開發一種指令專門來統一處理這種操作,就稱為Single Instruction Multiple Data。因此,就可以用一條指令來完成以前需要多條指令完成的任務,提升了效能。這種指令集對特定應用非常有用,比如影象和視訊處理、3D圖形渲染和各種科學計算程式碼等等。

     現在幾乎所有的體系結構都添加了SIMD指令集,比如SPARC (VIS), x86 (MMX/SSE/AVX), POWER/PowerPC (AltiVec) and ARM (NEON)。 

 

那篇文章下面的內容都是討論儲存器的問題,我就不多說了。。。。。