Intel, AMD及VIA CPU的微架構(26)
9. Sandy Bridge與Ivy Bridge流水線
Intel的代號為Sandy Bridge的微架構是Core2與Nehalem設計的進一步發展。在解碼器後添加了一個新的μop快取,浮點執行單元從128位擴充套件到256位。
Sandy Bridge有2-8核,某些版本能夠在每個核上執行兩個執行緒。
支援新的AVX指令集。這對浮點向量操作,把16個128位XMM暫存器擴充套件為256位YMM暫存器。在AVX指令集中,大多數XMM與YMM指令有非破壞性的三引數版本。
9.1. 流水線
Sandy Bridge與Ivy Bridge流水線非常類似於Core2與Nehalem,但在解碼器後添加了μop快取。
在Sandy Bridge上重排緩衝有168項,在Haswell上有192項。在Sandy Bridge上保留站有54項,在Haswell上有60項。根據Intel的公開資訊,Sandy Bridge有160個整數暫存器,144個向量暫存器;Haswell各有168個。
9.2. 指令獲取與解碼
預解碼器與解碼器每時鐘週期可以處理16位元組或4條指令,或者在巨集操作融合的情形下,5條指令。它看起來與Core2與Nehalem中的解碼器幾乎完全相同。
帶有任意數量字首的指令在一個時鐘週期裡解碼。
在某些情形下,長度改變字首(參考第93頁)的懲罰被消除了,仍有幾個情形還有:
- 使用一個運算元大小字首,帶有一個立即數的移動指令,即mov ax, 1234沒有懲罰。
- 使用一個運算元大小字首,帶有一個立即數的算術與邏輯指令,即add ax, 1234在解碼器中有2-3個時鐘週期的懲罰,不管是否對齊。這適用於32位或64位模式中,帶有一個16位常量運算元的所有算術與邏輯指令。
- 帶有一個16位運算元的NEG,NOT,DIV,IDIV,MUL與IMUL指令沒有懲罰。
- 地址大小字首不會導致懲罰,即使它改變了指令的長度。
有4個解碼器,根據特定的模式,可以處理產生多個μop的指令。以下指令模式在我的實驗中,可以在一個時鐘週期內被成功解碼:
- 1-1-1-1
- 2-1-1
- 3
- 4
產生3或4個μop的指令單獨解碼。生成超過4個μop的指令由效率較低的微程式碼處理。
在Sandy Bridge上,操作碼0F 1F的多位元組NOP指令僅能在四個解碼器中的第一個裡解碼,而帶有額外字首(操作碼66 66 90)的簡單NOP可在任一解碼器中處理。Ivy Bridge沒有這個限制。在Ivy Bridge上,這兩種長NOP以每時鐘週期4個的速度解碼。
9.3. μop快取
Sandy Bridge與Ivy Bridge在解碼器後有用於已解碼μop的一個快取。這是有用的,因為在獲取/解碼單元中每時鐘週期16位元組的限制是一個嚴重的瓶頸,如果瓶頸指令長度超過4位元組。對能放入μop快取的程式碼,吞吐率翻倍到每時鐘週期32位元組。
μop快取組織為32組、8路、6個μop,最大容量1536個μop。對程式碼每個對齊、連續的32位元組塊,它最多能分配3行,每行6個μop。
耗盡μop快取的程式碼不受獲取及解碼單元的限制。每時鐘週期,它可以提交4個(可能融合的)μop,或等同的32位元組程式碼。
μop快取很少用到最大容量的1536個μop。由於以下原因,使用率通常少於最優:
- 一個μop快取行分配給一個特定的32位元組程式碼塊。每次經過32位元組的程式碼時,將開始新的μop快取行,即使前面的快取行只是部分填充了。
- 產生多個μop的指令不能分開在兩個μop快取行間。如果當前行不能完全包含一條指令,那麼該行餘下部分將不會使用,這條指令將被放在新行的開頭。
- 產生多於4個μop的指令使用微程式碼ROM。這樣的指令使用一整個μop快取行。
- 無條件跳轉或呼叫總是終止一個μop快取行。
- 在μop快取中同一塊程式碼可以有多個入口,如果它有多個跳轉入口。
- 要求超過32位元儲存空間的指令在μop快取中可能佔據兩項,需要一個額外的時鐘週期載入。細節在下面給出。
- 每時鐘週期不能載入多個μop快取行。如果許多指令使用兩項,這可能是一個瓶頸。
- 產生超過18個μop,或在μop快取中要求超過18項的32位元組程式碼塊不會分配μop快取。
- 流水線在從解碼器獲取的μop與從μop快取獲取的μop之間頻繁切換。每次切換需要1個時鐘週期。
μop快取中的每項有32位元儲存空間用於地址與資料。一個μop可能需要超過32位元的儲存空間,比如它同時有一個記憶體運算元與一個立即數。系統使用各種方法來處理這個問題。32位的儲存空間可能分為兩個16位域,一個用於地址位元,一個用於立即數位元。一個32位地址可以儲存為一個16位符號擴充套件值,如果它在範圍-215到215。類似的,一個32位立即數可以儲存為一個16位符號擴充套件值,如果它在範圍-215到215,一個64位立即數可以儲存為一個32位符號擴充套件值,如果它在範圍-231到231。不過,一個64位地址域不能轉換為一個32位符號擴充套件值。
如果必須的資料不能壓縮到一個32位值或16位值,那麼如果可能,將從同一個μop快取行的其他項借未使用的資料空間。如果這不可能,在μop快取中將使用兩項。
μop快取行需要一個額外的時鐘週期來載入,如果它有使用額外資料空間的項,不管這額外的儲存空間是從另一個項借的,還是使用了額外的項,除了使用RIP相對取址的指令。
使用RIP相對取址的指令與其他指令的行為稍有不同。一個劣勢是,32位儲存空間不能分解為兩個16位域。另一方面,它有如果使用了儲存立即數的額外資料空間,無需額外時鐘週期來載入的優勢。
根據我的測試得到的細節,在下面的表9.1中給。
地址模式 |
地址或偏移位元 |
立即數位元 |
μop快取項數 |
μop快取載入時間 |
沒有記憶體運算元 |
0 |
0, 8, 16, 32 |
1 |
1 |
沒有記憶體運算元 |
0 |
64small |
1 |
1 |
沒有記憶體運算元 |
0 |
64 |
2 |
2 |
32位絕對地址 |
32small |
0, 8, 16, 32small |
1 |
1 |
32位絕對地址 |
32small |
32 |
2 |
2 |
32位絕對地址 |
32 |
0 |
1 |
1 |
32位絕對地址 |
32 |
8, 16, 32 |
2 |
2 |
指標,索引或兩者 |
0, 8, 16, 32small |
0, 8, 16, 32small |
1 |
1 |
指標,索引或兩者 |
0, 8, 16, 32small |
32 |
2 |
2 |
指標,索引或兩者 |
32 |
0 |
1 |
1 |
指標,索引或兩者 |
32 |
8, 16, 32 |
2 |
2 |
Rip相對 |
32small, 32 |
0 |
1 |
1 |
Rip相對 |
32small, 32 |
8, 16, 32 |
2 |
1 |
64位絕對地址 |
64small, 64 |
0 |
2 |
2 |
表9.1. μop快取中不同指令的大小
注:32small表示一個範圍在-215到215的32位值,64small表示一個範圍在-231到231的64位值。
例子:
; Example 9.1. Instructions in μop cache
mov dword [rsi+4], 1000H ; one entry
mov dword [rsi+4], 10000H ; two entries
mov dword [rsi+40000H], 0 ; two entries
mov dword [rsi+40000H], eax ; one entry
cmp dword fs:[8], 2 ; one entry
cmp dword fs:[8], 20000H ; two entries
cmp dword fs:[80000H], 2 ; two entries
mov rax,-10000000H ; one entry (even if long form)
mov rax,-100000000H ; two entries
vinsertf128 ymm0,ymm1,[rip+x],1 ; two entries
由於μop快取帶來的效能增益相當可觀,如果平均指令長度超過4位元組。可以考慮以下優化μop快取的方法:
- 確保關鍵迴圈足夠小,能放入μop快取。
- 將最重要的迴圈項與函式項對齊到32位元組邊界。
- 避免不必要的迴圈展開。
- 根據表9.1,避免有額外載入時間的指令。
- 根據表9.1,要求額外資料空間的指令可與不使用資料空間的指令混用,使得有空閒的資料空間可借。
- 如果一個關鍵迴圈的32位元組程式碼塊產生超過18個μop,或者不能放入μop快取行,那麼重新組織程式碼;或者使某些指令更長,如果這使得它能放入3個μop快取行,可能是有用的。這避免了在μop快取與解碼器之間切換的代價。
9.4. 迴路緩衝
Sandy Bridge與Ivy Bridge裡保留了Nehalem裡的28個μop的迴圈緩衝(參考第92頁)。迴圈緩衝放在μop快取之後,但在μop快取不命中時,它也可以從解碼器接收μop。迴圈快取提升了由於各種原因在μop快取裡執行不良的小迴圈的效能。在μop快取不是瓶頸的地方,事實上,這是大多數情形,迴圈緩衝沒有可察覺的效果。
使迴圈小於28個μop,以利用迴圈快取,是有益的。
9.5. 微操作融合
μop融合與之前的處理器一樣。某些在執行單元中需要2個μop的指令可以使用μop融合技術將這兩個μop作為一個μop穿過流水線的大部分,以節省流水線頻寬。參考第80頁與94頁。
通過查詢手冊4“指令表”,可以看到哪些指令使用μop融合。使用μop融合的指令在“非融合域”列出的μop數高於“融合域”列出的μop數。
9.6. 巨集操作融合
在比之前處理器更多的情形下,Sandy Bridge與Ivy Bridge可以將兩條指令融合為一個μop(參考第95頁)。
在特定情形下,解碼器可以將一條算術或邏輯指令與一條後續的條件跳轉指令融合為一個計算-跳轉μop。在執行單元處,計算-跳轉μop不分解,由執行埠5的分支單元作為一個μop執行。
CMP,ADD與SUB指令可與有符號及無符號分支指令融合。INC與DEC可與有符號分支指令融合,TEST與AND指令可與所有分支指令(包括無用的組合)融合,如下表所示:
第一條指令 |
可與這些融合(反之亦然) |
不能融合 |
cmp |
jz, jc, jb, ja, jl, jg |
js, jp, jo |
add, sub |
jz, jc, jb, ja, jl, jg |
js, jp, jo |
adc, sbb |
none |
|
inc, dec |
jz, jl, jg |
jc, jb, ja, js, jp, jo |
test |
all |
|
and |
all |
|
or, xor, not, neg |
none |
|
shift, rotate |
none |
|
表9.2. 指令融合
第一條指令可以有一個立即數或記憶體源運算元,但不能是兩者。它不能有記憶體目標運算元。不能有RIP相對取址運算元。例子:
; Example 9.2. Instruction fusion
dec ecx
jnz L1 ; Fusion is possible
cmp dword ptr [esi], 0
je L2 ; No fusion. Both memory operand and immediate
dec dword ptr [esi]
jnz L3 ; No fusion. Memory destination operand
add eax, ebx
jo L4 ; No fusion. See table 9.2
cmp eax,[mem] ; Will use RIP-relative address in 64-bit mode
jg L5 ; Fusion only in 32 bit mode
JECXZ與LOOP指令不能融合。在Ivy Bridge上,指令融合甚至對跨越16位元組邊界的指令也能工作。不確定Sandy Bridge是否也適用。
如果在同一時鐘週期,多個可融合指令對到達4個解碼器,那麼僅第一個對被巨集融合。
程式設計師應該將可融合的算術指令與一條後續的條件跳轉指令放在一起,而不是在它們之間排程其他指令;利用巨集操作融合,最好在一個可融合對與下一個可融合對之間有至少3條其他指令。
指令融合可以增加吞吐率最大到每時鐘週期5條指令。不幸的,它也會降低吞吐率。可融合算術/邏輯指令(ADD,SUB,INC,DEC,CMP,AND,TEST)的解碼速度比相似不可融合指令(如OR)低。可能的解釋是:如果任何可融合算術/邏輯指令進入最後一個解碼器,解碼將被推遲,該指令將在下一個時鐘週期進入第一個解碼器,以檢查下一條是否為可融合的分支指令。這意味著,對這些不能放入μop快取的程式碼指令,解碼吞吐率降低,即使程式碼不包含分支。
9.7. 棧引擎
Sandy Bridge有一個專用的棧引擎,它與之前處理器上的工作方式相同,如第81頁所述。
PUSH,POP,CALL及RET指令對棧指標的修改由一個特殊的棧引擎來完成,在流水線中,它緊跟在解碼階段之後,可能在μop快取前面。這將流水線從修改棧指標μop的負擔中解放出來。這個機制節省了棧指標的兩次拷貝:一次在棧引擎中,另一次在暫存器檔案與亂序核。這兩個棧指標可能需要同步,如果PUSH,POP,CALL及RET指令序列後跟著一條直接讀或修改棧指標的指令,比如ADD ESP, 4或MOV EAX, [ESP+8]。在需要兩個棧指標同步的每個情形裡,棧引擎插入一個額外的棧同步μop。更多細節參考第81頁解釋。
9.8. 暫存器分配與重新命名
所有整數、浮點、MMX、XMM、YMM、標記以及還可能段暫存器都可以重新命名。浮點控制字也可以重新命名。
暫存器重新命名由顯示在圖6.1中的暫存器別名表(RAT)及重排緩衝(ROB)控制。來自解碼器與棧引擎的μop通過一個佇列去往RAT,然後到ROB-讀及保留站。RAT每時鐘週期可以處理4個μop。每時鐘週期RAT可以重新命名4個暫存器,甚至重新命名同一個暫存器四次。
無關的特殊情形
將一個暫存器置零的一個常用方式是通過與自身XOR或減去自身,比如XOR EAX, EAX。處理器知道,如果兩個運算元暫存器是相同的,特定的指令與暫存器之前的值無關。
這適用於以下所有指令:XOR,SUB,PXOR,XORPS,XORPD,VXORPS,VXORPD以及PSUBxxx與PCMPGTxx的各個版本,但不適用CMP,SBB,PANDN。
暫存器在重新命名階段由這些指令置零。這些置零指令的吞吐率是每時鐘週期4條,因為不使用執行單元。
如果兩個暫存器相同,PCMPEQxx指令將所有位元置1。這條指令被認為與暫存器之前的值無關,但它需要一個執行單元。因為與x87形式的浮點棧暫存器重疊,帶有一個64位mmx暫存器的置零指令也使用一個執行單元。
無需執行單元的指令
上述暫存器由例如XOR EAX, EAX的指令置零的特殊情形,在暫存器重新命名/分配階段處理,無需使用任何執行單元。這使得這些置零指令及其高效,吞吐率為每時鐘週期4條置零指令。進位標記可由CLC以同樣高效的方式置零。
之前的處理器僅可以在暫存器重新命名階段處理FXCH指令。Sandy Bridge還可以在暫存器重新命名/分配階段處理這些特殊的置零指令,連同NOP指令。
因此,NOP指令,包括多位元組NOP非常高效,吞吐率為4個NOP每時鐘週期。出於效率的原因,使用多位元組NOP比常用的偽NOP,比如MOV EAX, EAX或LEA RAX, [RAX+0],要好得多。
消除移動指令
Ivy Bridge(但Sandy Bridge不是)可以在暫存器分配階段消除暫存器到暫存器的移動。下面的例子展示了這:
; Example 9.3. Move elimintaion
add eax,4
mov ebx,eax ; this move can be eliminated
sub ebx,ecx
在這個例子中,暫存器重新命名可能會消除mov ebx, ebx指令。在第三條指令中代表ebx輸入的物理暫存器就是代表第一條指令中eax輸出值的暫存器。暫存器重新命名在第4頁解釋。
移動消除不總是成功的。在必要的運算元未就緒時,它會失敗。但在超過80%可能的情形裡,通常能成功消除移動。串接的移動也可以消除。
所有32位與64位通用暫存器以及所有128位與256位暫存器的移動消除是可能的。
從8位暫存器到32位或64為暫存器的零擴充套件移動也可以消除,比如MOVZX EAX, BL。16位暫存器的零擴充套件移動不能消除。目標是8位暫存器、16位暫存器或mmx暫存器的移動不能消除。符號擴充套件移動不能消除。
到暫存器自身的移動不能消除,例如mov eax, eax是不能消除的。
一個被消除的移動有0時延,不使用任何執行埠。但消耗解碼器頻寬。
9.9. 暫存器讀暫停
自Pentium Pro起,暫存器讀暫停是一個嚴重的、通常被忽視的瓶頸。所有基於P6微架構及其後繼Pentium M、Core與Nehalem微架構的Intel處理器,有每時鐘週期從永久暫存器檔案2或3次讀的限制。
在Sandy Bridge與Ivy Bridge中,這個瓶頸被最終消除。在我的實驗裡,我沒找到暫存器讀次數的實際限制。
9.10. 執行單元
Sandy Bridge與Ivy Bridge有6個執行埠。埠0,1與5用於算術與邏輯操作(ALU)。在埠2與3上有兩個相同的記憶體讀埠,之前的處理器只有一個。埠4用於記憶體寫。埠4上的記憶體寫單元沒有地址計算。所有寫操作使用埠2或3計算地址。最大吞吐率是每埠、每時鐘週期一個非融合μop。
埠0,1與5支援完整的256位向量操作。埠2,3與4對256位讀寫操作使用兩個時鐘週期。各單元在下面的表9.3與9.4中列出。
在通用暫存器與向量暫存器中的乘法有不同的埠。通用暫存器乘法器在埠1上,時延為3。整數與浮點向量乘法器在埠0上,對所有精度時延都是5。這兩個乘法器可以同時執行,兩者都完全流水線化,吞吐率為每時鐘週期1個向量操作。
整數除法使用埠0上的浮點除法。這是僅有的沒有流水線化的單元。
埠5上的跳轉單元處理所有跳轉與分支操作,包括巨集融合的計算-分支操作。
對整數與浮點向量操作,處理器有不同的執行單元。例如,浮點向量移動(MOVAPS與MOVAPD)僅在埠5上執行,而整數向量移動(MOVDQA)可以在埠0,1或5上執行。
執行單元均勻分佈在埠0,1與5之間。這使得每時鐘週期執行3條向量指令成為可能,例如浮點向量乘法在埠0上。浮點向量加法在埠1上,浮點混排在埠5上。
執行埠 |
資料型別 |
操作 |
最大資料大小,位元 |
時延,時鐘 |
0 |
gp與ivec |
移動 |
128 |
1 |
1 |
gp與ivec |
移動 |
128 |
1 |
5 |
gp與ivec |
移動 |
128 |
1 |
0 |
gp與ivec |
add |
128 |
1 |
1 |
gp |
add |
64 |
1 |
5 |
gp與ivec |
add |
128 |
1 |
0 |
gp與ivec |
Boolean |
128 |
1 |
1 |
gp與ivec |
Boolean |
128 |
1 |
5 |
gp與ivec |
Boolean |
128 |
1 |
0 |
ivec |
multiply |
128 |
5 |
1 |
gp |
multiply |
64 |
3 |
0 |
gp |
shift |
64 |
1 |
1 |
ivec |
shift |
128 |
1 |
5 |
gp |
shift |
64 |
1 |
0 |
ivec |
shuffle, pack |
128 |
1 |
5 |
ivec |
shuffle, pack |
128 |
1 |
5 |
gp |
jump |
64 |
1 |
5 |
浮點 |
fp mov, shuffle |
256 |
1 |
1 |
浮點 |
fp add |
256 |
3 |
0 |
浮點 |
fp mul |
256 |
5 |
0 |
浮點 |
fp div與sqrt |
128 |
10-22 |
5 |
浮點 |
fp boolean |
256 |
1 |
2 |
所有 |
記憶體讀 |
128 |
|
3 |
所有 |
記憶體讀 |
128 |
|
4 |
所有 |
記憶體讀 |
128 |
|
表9.3. Sandy Bridge裡的執行埠
資料型別:gp = 通用暫存器,ivec = 整數向量
執行埠 |
資料型別 |
操作 |
最大資料大小,位元 |
時延,時鐘 |
0 |
gp與ivec |
移動 |
128 |
1 |
1 |
gp與ivec |
移動 |
128 |
1 |
5 |
gp與ivec |
移動 |
128 |
1 |
0 |
gp |
add |
64 |
1 |
1 |
gp與ivec |
add |
128 |
1 |
5 |
gp與ivec |
add |
128 |
1 |
0 |
gp與ivec |
Boolean |
128 |
1 |
1 |
gp與ivec |
Boolean |
128 |
1 |
5 |
gp與ivec |
Boolean |
128 |
1 |
0 |
ivec |
multiply |
128 |
5 |
1 |
gp |
multiply |
64 |
3 |
0 |
gp與ivec |
shift |
128 |
1 |
5 |
gp |
shift |
64 |
1 |
1 |
ivec |
shuffle, pack |
128 |
1 |
5 |
ivec |
shuffle, pack |
128 |
1 |
5 |
gp |
jump |
64 |
1 |
5 |
浮點 |
fp mov, shuffle |
256 |
1 |
1 |
浮點 |
fp add |
256 |
3 |
0 |
浮點 |
fp mul |
256 |
5 |
0 |
浮點 |
fp div and sqrt |
128 |
10-20 |
5 |
浮點 |
fp boolean |
256 |
1 |
2 |
所有 |
記憶體讀 |
128 |
|
3 |
所有 |
記憶體讀 |
128 |
|
4 |
所有 |
記憶體寫 |
128 |
|
表9.4. Ivy Bridge裡的執行單元
資料型別:gp = 通用暫存器,ivec = 整數向量,浮點 = 浮點暫存器與浮點向量
整數向量操作的時延與通用暫存器裡的操作相同。這使得在沒有足夠通用暫存器時,將MMX暫存器或XMM暫存器用於簡單的整數操作是便利的。雖然支援向量操作的執行單元較少。
讀寫頻寬
處理器有兩個相同的記憶體讀埠(埠2與3),之前的Intel處理器僅有一個讀埠。埠2和3還用於計算地址。所有的記憶體寫操作要求兩個μop:一個用於在埠2或3上計算地址,一個用在埠4上執行寫。
128位或更小的記憶體操作有每時鐘週期兩個讀或一個寫。因為僅有兩個地址計算單元(埠2與3),每時鐘週期不可能進行兩次讀及一個寫。
對在新的YMM暫存器裡的256位運算元,情形是不一樣的。對一個256位讀,每個讀埠需要兩個時鐘週期(但僅一個μop),對256位寫寫埠需要兩