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