Ⅰ CPU內部寄存器組結構及其功能是什麼
1.什麼是寄存器 所謂寄存器(register),它是CPU內部用來存放數據的一些小型存儲區域,用來暫時存放參與運算的數據襪沒和運算結果。其實寄存器就是一種常用的時序邏輯電路,但這種時序邏輯電路只包含存儲電路。寄存器的存儲電路是由鎖存器或觸發器構成的,因為一個鎖存器或觸發器能存儲1位二進制數,所以由N個鎖存器或觸發器可以構成N位寄存器。 2.寄存器與CPU指令 在講CPU的寄存器之前,我們先了解一下CPU指令系統。指令系統指的是一個CPU所能夠處理的全部指令的集合,Athlon XP和P4都是基於x86指令集,這是CPU的根本屬性,決定CPU運行什麼樣的程序。 指令一般分為:算術邏輯運算指令、浮點運算指令、位操作指令及其他的一些非運算指令,其中整數、地址、指令指針和浮點數據是按照數據形式來劃分的。通常我們把需要CPU進行不同處理的單個數據稱為標量數據(Scala Data)。標量數據既可以是整數數據,也可以是浮點數據。其中整數標量數據的存放區一般為通用寄存器(GPR),浮點標量數據的存放區一般為浮點寄存器(FPR)。與標量數據相對的是矢量數據(Vector Data),所謂矢量數據就是指一列需要由處理器作相同處理的數據集合。比如處理器在做MP3編碼的過程中,需要對內存中的音頻文件里的各位元組數據作相同的MP3編碼操作。那麼通常使用MMX或SSE這類單指令多數據流(SIMD)指令,將數個位元組打包為一組矢量數據,存放在MMX或SSE寄存器中,再送往相應的功能單元進行統一操作。 其中通用寄存器是處理器中最快的存儲器,用來保存參加運算的操作數和中間結果。在通用寄存器的設計上,RISC與CISC(也就是我們常說的x86架構)有著很大的不同。CISC的寄存器通常很少——只有8個通用寄存器。由於CPU在執行指令過程中,存在指令依賴性,在一定程度上使得x86 CPU不能在每個時鍾周期中立即發布大量的指令。所謂「依賴性」就是巧爛指令的執行需要前個指令的運算結果。比如程序員經常使用的分支程序,請看下面這個例子: A=C*1 B=A+2 只要變數A的值還不知道,B=A+2就不能進行運算。也就是說,只要指令1的結果沒有寫進寄存器,CPU調度器就不能把指令2發布到執行單元。由於程序分支會造成具有較長流水線CPU運行停滯的,目前常用的解決方法是採用分支預測。 不過,分支預測同樣存在一個問題:流水線越長,指令潛伏期也越長,等待前一指令運算結果的時間也越長,同樣會造成CPU運行停滯。我們知道,程序指令通常都有各類型的條件分支語句,通過驗證條件決定執行路線。但CPU執行單元內是通過一項特殊的預測機制選擇一條路線直接執行(這樣可以避免驗證語句條件而處於等待情況),然後在後面進行驗證。如果預測正確則繼續往下執行,如果發現以前的預測錯誤,那麼就必須返回原地重新開始,以前的指令就會作廢。 因此,管線越長告寬納,意味著出現分支預測錯誤的機會就越多,越多在管線內的指令會被清除掉,而且重新讓管道填滿指令的時間也會越長。對於普通處理器來說,如果出現分支預測錯誤,CPU就不得不將整條流水線清空後從錯誤的地方重新裝滿數據、重新執行。毫無疑問這將花更多的時間,整體性能就會下降。因此,針對通用寄存器少的問題,在x86架構中比較完美的解決方法就是增加寄存器的數量和採用「亂序執行」。3.為什麼寄存器不夠用 在上面我們已經提到,寄存器只是用來暫時存放指令值的,如果CPU需要把兩個值加起來,它需要用1個寄存器來存放運算結果,用2個寄存器來存放相加的數值。例如,在以下的方程式中:A = 2 + 4 * 在寄存器1儲存「2」; * 在寄存器2儲存「4」; * 在寄存器3儲存「寄存器1 + 寄存器 2」; 因為在微處理器裡面有超過3個寄存器,因此這個運算能夠輕易地執行,不會造成用光寄存器的情況。在這些運算被執行之後,所有的3個數值都能夠被保留並重新使用,因此如果我們再想在結果加上2的話,處理器只需要執行:寄存器 1 + 寄存器 3 就可以了。如果微處理器僅有2個剩餘的寄存器,而我們又需要再次使用2和4的值,那麼這些值在覆蓋結果A之前,必須儲存在主內存之中 。運算執行的過程則會變成如下所示: * 在寄存器1儲存 「2」; * 在寄存器2儲存「4」; * 在主內存的某個空間儲存「寄存器1 + 寄存器2」; 我們可以看到這里使用了其它的內存訪問過程,而在這期間其實還有我們沒有提到的其它處理過程,比如主內存的定位也需要佔據寄存器,以便讓CPU 告訴裝載/儲存單元該往哪裡發送數據 。如果我們需要使用到這些結果的話,那麼CPU將不得不首先到主內存中找回這些結果,把目前滿載的寄存器驅逐一些數據,把它們寫入主內存,然後再把尋找到的數據儲存在寄存器里。 這里大家應該能夠明白吧,對內存的訪問次數將會可怕地增加;你需要訪問內存的時間越多,那麼處理器等待工作完成的時間就越長——因而造成性能的下降。因此面對超標量CPU在並行處理大量運算,x86體系僅有的8個通用寄存器遠遠不能滿足需要,在同一時鍾周期中,如果有3個指令發布,你就需要3個輸出寄存器和6個輸入寄存器。我們該怎麼辦呢?聰明的工程師們發現了突破這個限制的方法:「寄存器重命名」。 4.寄存器重命名技術 寄存器重命名,是CPU在解碼過程中對寄存器進行重命名,解碼器把「其它」的寄存器名字變為「通用」的寄存器名字,本質上是通過一個表格把x86寄存器重新映射到其它寄存器,這樣可以讓實際使用到的寄存器遠大於8個。這樣做的好處除了便於前面指令發生意外或分支預測出錯時取消外,還避免了由於兩條指令寫同一個寄存器時的等待。 下面我們以一個超標量CPU執行8個算術指令為例:假設它在每個時鍾周期中能對2個指令解碼,引出計算結果是在指令發布後3個時鍾周期發生的: (1)在第1個時鍾周期,兩個指令發布:它們互不關聯,因此,它們將在3個時鍾周期後(第4個時鍾周期)引出; (2)在第2個時鍾周期,我們首次遇到了「指令依賴」,指令3需要指令2的結果,此時指令3不能開始發布; (3)如果是按序執行,指令4、5、6就不能在指令3前發布。只有在第5個時鍾周期時(指令2的結果已得到)才能發布指令3; (4)在第6個時鍾周期有個大問題:我們想把結果寫到寄存器R1,但這將改變指令5的結果。因此,我們只有在R1空閑時(第10個時鍾周期)才能發布指令6。 按照正常情況處理的話,盡管這個CPU每個時鍾周期可以對2個指令解碼,但它每個時鍾周期的指令執行數只有0.53。如果每次程序所需的寄存器正被使用,我們可以把數據放到其它的寄存器中,在第6個時鍾周期將寄存器R1重命名,指令6和指令8不再耽誤CPU的工作。結果是我們能夠將每個時鍾周期的指令執行數提高50%。寄存器重命名技術可以使x86 CPU的寄存器可以突破8個的限制,達到32個甚至更多。寄存器重命名技術現在已經深深地紮根於超標量CPU中了。5.亂序執行技術 除此之外,處理器工程師還引入了亂序執行技術,從一定程度上來緩解通用寄存器不足的問題。採用亂序執行技術的目的是為了使CPU內部電路滿負荷運轉並相應提高了CPU運行程序的速度。 這好比請A、B、C三個名人為春節聯歡晚會題寫橫幅「春節聯歡晚會」六個大字,每人各寫兩個字,如果這時在一張大紙上按順序由A寫好「春節」後再交給B寫「聯歡」,然後再由C寫「晚會」,那麼這樣在A寫的時候,B和C必須等待,而在B寫的時候C仍然要等待而A已經沒事了。但如果採用三個人分別用三張紙同時寫的做法,那麼B和C都不必等待就可以同時各寫各的了,甚至C和B還可以比A先寫好也沒關系(就像亂序執行),但當他們都寫完後就必須重新在橫幅上按「春節聯歡晚會」的順序排好(自然可以由別人做,就象CPU中亂序執行後的重新排列單元)才能掛出去。 不過,雖然採用寄存器重命名技術、亂序執行技術,但仍不能從根本上解決x86處理器通用寄存器不足的問題。以寄存器重命名技術來說,這種技術的寄存器操作相對於RISC來說,要花費一個時鍾周期來對寄存器進行重命名,這無形中降低了處理器性能以及流水線工作效率,也增加了程序和編譯器的優化難度。針對這個問題,最新的x86-64架構中(K8處理器),AMD在x86架構基礎上將通用寄存器和SIMD寄存器的數量增加了1倍:其中新增了8個通用寄存器以及8個SIMD寄存器作為原有x86處理器寄存器的擴充。 這些通用寄存器都工作在64位模式下,經過64位編碼的程序就可以使用到它們。這些64位寄存器稱為RAX、RBX、RCX、RDX、RDI、RSI、RBP、RSP、RIP以及EFLAGS,在32位環境下並不完全使用到這些寄存器,同時AMD也將原有的EAX等寄存器擴展至64位的RAX,這樣可以增強通用寄存器對位元組的操作能力。從擴充方式上看,EAX等寄存器可以看做是RAX的一個子集,系統仍然可以完整地執行以往的32位編碼程序。增加通用寄存器除了可高效存儲數據外,還可作為定址時的地址指針,從而縮短指令長度和指令執行時間,加快CPU的運算處理速度,同時也給編程帶來方便。 此外,為了保證K8的分支預測更有效率,K8的分支預測寄存器增加到64個。分支指令可以被設為真或假,而每個指令中的6位被分配到單獨一個預測寄存器中,只有預測寄存器被設定為「真」時,那些指向預測寄存器為「真」的指令結果才會被執行。其次由於所有的分支都能並行執行,CPU所花的時間同只執行單個分支的時間是相同的,降低了預測出錯的風險。第三由於CPU不再跳躍執行,它不會把程序代碼分成小塊。也就是說,稍前和稍後的程序代碼可以打包。這樣CPU能夠一並將它們發布,增大並行工作量。從而使性能提高10%~15%,特別是在整數代碼部分。 不過在x86-64中,寄存器的擴展部分似乎僅對於整數、地址數據有效。對浮點和向量數據則仍然保持原樣。我們能從K8向64位的擴展所獲得的好處,只不過是可以在同樣一條指令中,處理更大數值的整數數值以及管理空間更大的內存區域而已。而在32位的情況下,由於通用寄存器只能容納最大32位的數據,因此顯然要花費更多條指令對尺寸超過32位的數據進行處理。這種改進對伺服器、科學計算這樣的領域具有一定的意義,但顯然並不是普通家用環境急需的改進。 可以說,處理器的寄存器對處理器的性能有著巨大的影響。但是無論怎麼發展,通用型CPU目前還沒有脫離x86架構的限制,也許有一天,新的寄存器技術能讓我們的CPU變得更加功能強大!