[譯]Intel, AMD及VIA CPU的微架構(28)
9.16. Sandy Bridge與Ivy Bridge中的瓶頸
指令獲取與預解碼
指令獲取與解碼非常類似於之前的處理器,仍然是一個瓶頸。幸運的是,新的 μop 快取降低了解碼器的壓力。
μop 快取
新的 μop 快取有了顯著的提高,因為在程式碼的關鍵部分適合 μop 快取時,它消除了指令獲取與解碼的瓶頸。現在,很容易得到每時鐘週期 4 條指令的最大吞吐率(或者巨集融合的 5 條),即使對更長的指令。
程式員應該小心在 CPU 密集程式碼中經濟地使用 μop 快取。適合 μop 快取的迴圈與不適合 μop 快取的迴圈之間的效能差異相當可觀,如果平均指令長度超過 4 位元組。
在舊的 P4/NetBurst 處理器中, μop 快取有點類似追蹤快取(參考第 39 頁),具有某些相同的弱點。地址與資料運算元需要超過 32 位儲存的指令可能在 μop 快取中使用額外的空間,且需要額外一個時鐘週期載入。優化程式碼避免這些弱點是可能的,但這不可能由任何人來完成,而是最專業的彙編程式設計師。
對我而言,為什麼處理器沒有在程式碼快取中標記指令邊界是一個謎。這將消除指令長度解碼的瓶頸,因而消除 μop 快取的需要。 AMD 處理器這樣做,舊的 Pentium MMX 也同樣。
暫存器讀暫停
這個舊的瓶頸,自 Pentium Pro 起困擾 Intel 處理器,最終被消除了。在我的測量中沒有檢測到這樣的暫停。
執行埠與執行單元
執行埠與執行單元的能力相當高。許多 μop 可在 2 個或 3 個執行埠間選擇,每個單元每時鐘週期可處理一個完整的 256 位向量操作。因此,如果指令平均分配在埠之間,執行埠的吞吐率不是一個嚴重的瓶頸。
如果程式碼產生許多去往相同執行埠的 μop ,執行埠會是平均。
執行時延與依賴鏈
執行時延通常是低的。大多數整數 ALU 操作僅有一個時鐘週期的時延,即使對 256 位向量操作。執行時延僅在長的依賴鏈中是關鍵的。
部分暫存器訪問
在寫暫存器一部分後,讀整個暫存器有一個懲罰。使用 MOVZX 或 MOVSX 將 8 位或 16 位記憶體運算元讀入一個 32 位暫存器,而不是使用暫存器更小的部分。
回收
在我的所有測試中,都沒有觀察到 μop 的回收成為一個瓶頸。
分支預測
分支預測器沒有專門識別迴圈。帶有許多分支迴圈的預測遜色於之前的處理器。分支歷史模式表與分支緩衝可能比之前的處理器大,但誤預測仍然常見。對適合 μop 快取的程式碼,誤預測懲罰要更小。
記憶體訪問
Sandy Bridge 有兩個記憶體讀埠,而之前的處理器僅有一個。這是一個重要的改進。在最大記憶體頻寬利用率時,快取庫衝突相當常見。
Ivy Bridge 預取指令有一個嚴重的問題,它極慢。
多執行緒
大多數關鍵資源線上程間共享。這意味著在多執行緒應用程式中,瓶頸變得更加關鍵。
文獻
"Intel 64 and IA-32 Architectures Optimization Reference Manual". Intel 2011.
10. Haswell與Broadwell流水線
在之前設計之上, Haswell 有幾個重要的改進。資料快取的頻寬加倍到每時鐘週期兩次讀及一次寫,每次最多 32 位元組。執行單元數從 6 增加到 8 。大多數執行單元有每時鐘週期一整個 256 位向量的頻寬。
Haswell 目前有 2 – 18 個核,大多數版本每個核能執行兩個執行緒。大多數關鍵資源在執行在同一核上的執行緒間共享,如第 119 頁對 Sandy Bridge 與 Ivy Bridge 處理器所述。
Haswell 是第一個支援 AVX2 指令集的處理器,它可以處理 256 位向量的整數指令。它還是第一個支援融合乘加( FMA )指令的處理器。
Broadwell 是 Haswell14 nm 微縮製程( die shrink ),僅在執行單元上有少數改進。
10.1. 流水線
流水線類似於之前的設計,但有更多的改進。它為每時鐘週期 4 條指令的吞吐率而設計。
根據下面第 頁列出文獻,每個核有一個 192 項的重排緩衝,保留站有 60 項,暫存器檔案有 168 整數暫存器與 168 向量暫存器。
流水線的所有部分在那些可以在每個核上執行兩個執行緒的 CPU 模式中共享。在兩個執行緒在同一個核上執行時,每個執行緒得到一半的總吞吐率。
10.2. 指令獲取與掩碼
在單執行緒應用程式中,指令獲取單元每時鐘週期可以獲取最多 16 位元組的程式碼。
有 4 個解碼器,它們可以處理每時鐘週期最多產生 4 個 μop的指令,以第108頁所描述的Sandy Bridge的方式。
具有任意字首的指令在一個時鐘週期內解碼。對重複字首沒有懲罰。
長度改變字首的懲罰與 Sandy Bridge 相同(參考第 108 頁)。帶有一個立即數,使用運算元大小字首的算術與邏輯指令,如 add ax, 1234 在解碼器中有 2 – 3 個時鐘週期的懲罰,不管是否對齊。這適用於在 32 或 64 位模式中,使用一個 16 位立即數作為運算元的算術與邏輯指令。移動指令沒有長度改變字首的懲罰。
10.3. μop快取
來自 Sandy Bridge 的 μop快取(參考第109頁),在Haswell與Broadwell中保留了相同的大小與結構。μop快取最重要的影響是。吞吐率不再受限於小程式碼迴圈每時鐘週期16位元組的獲取速度。適合μop快取的程式碼片段可以每時鐘週期最多32位元組的速率釋出。當平均指令長度超過4位元組時,這是一個大的優勢。
Sandy Bridge μop快取的侷限與弱點也適用於Haswell與Broadwell。細節參考第109頁。
10.4. 迴路緩衝
處理器有隻是從 μop佇列回收μop的迴路緩衝。迴路緩衝很少用掉佇列的所有56項,但最多30個μop,或者有時最多40個μop的小迴圈將從迴路緩衝受益。μop佇列動態地線上程間共享,因此如果兩個執行緒執行在同一個核上,每個僅得到一半的大小。迴路緩衝給出每時鐘週期4個μop的穩定吞吐率,不管小迴圈的指令長度。
概括來說,流水線有三個不同的來源,依賴於關鍵迴圈的大小:
- 迴路緩衝用於最多 30 – 40 條指令的小迴圈。吞吐率是每時鐘 4 個 μop,沒有指令長度限制。
- μop快取用於最多大約1000條指令的迴圈。吞吐率是每時鐘最多4條指令或32位元組程式碼。
- 獲取及解碼單元用於不在 μop快取的指令。吞吐率是每時鐘最多4條指令或16位元組程式碼。
融合指令對(參見下面)在μop快取及迴路緩衝中算作一個。每時鐘週期兩個融合非採用分支,從迴路緩衝或μop快取獲取每時鐘週期6條指令的最大吞吐率是可能的。
在μop 3個來源間,分支誤預測懲罰可能不同,但我不能證實這樣一個差異,因為測量的誤差很高。在所有3個情形裡,測的的誤預測懲罰在16到20個時鐘週期間。
10.5. 微操作融合
與之前處理器相同的方式使用μop融合。某些在執行單元中需要兩個μop的指令可以使用μop融合技術,從解碼器到保留站,將這兩個μop保持為一個,以節省流水線頻寬。然後,保留站將釋出兩個μop到兩個不同的埠。大多數記憶體寫指令以及大多數帶有記憶體運算元的算術與邏輯指令使用μop融合,不管暫存器的大小。進一步解釋參考第80頁與94頁。
解碼器每時鐘週期可以處理4條μop融合指令。
在手冊4“指令表”的表格中,可以查到哪些指令使用μop融合。使用μop融合的指令在“非融合域”下列出的μop數要大於“融合域”下列出的μop數。
10.6. 巨集操作融合
Haswell與Broadwell可以像之前處理器那樣,將兩條指令融合為一個μop(參考第95頁)。
在某些情形裡,解碼器將1條算術或邏輯指令與後續的條件跳轉指令融合為1個計算分支μop。計算分支μop在執行單元處不分解為2個μop,而是由埠0或6處的分支單元作為1個μop執行。
CMP , ADD 與 SUB 指令可與有符號及無符號分支指令融合。 INC 與 DEC 可與有符號分支指令融合,而 TEST 與 AND 指令可與所有的分支指令(包括無用的組合)融合,如第 111 頁表 9.2 所示。
第一條指令可以有一個立即數或記憶體源運算元,但不能是兩者。它不能有記憶體目標運算元。它不能有 RIP 相對取址運算元。 JECXZ 與 LOOP 指令不能融合。
不像之前的處理器,即使跨越 16 位元組程式碼邊界,也可以融合。兩個可融合對可在同一個時鐘週期裡解碼。
要利用巨集操作融合,程式設計師應該將任何可融合算術指令與後續的跳轉指令放在一起,而不是在之間安排其他指令。所有 4 個解碼器都支援巨集操作融合。
10.7. 棧引擎
Haswell 與 Broadwell 有一個類似於 Sandy Bridge 的棧引擎,如第 112 頁所述。在棧操作,像 push , pop , call 或 return ,穿插著明確訪問棧指標的指令,如 rsp, 8 或 mov eax, [rsp+16] 時,一個額外的棧同步 μop被自動插入。
10.8. 暫存器分配與重新命名
所有整數、浮點、 MMX 、 XMM 、 YMM 、標記以及還可能段暫存器可以重新命名。浮點控制字也可以重新命名。
暫存器重新命名由重排緩衝以及排程器控制。沒有觀察到暫存器分配與重新命名成為瓶頸。
依賴性的特殊情形
將一個暫存器置零的常見方式是與自身 XOR ,或減去自己,如 XOR EAX, EAX 。處理器知道,如果兩個輸入運算元是同一個暫存器,特定的指令與暫存器的之前值無關。這個暫存器在暫存器分配階段置零,無需使用執行單元,也無需等到暫存器之前的值可用。
以下指令可以這個方式將一個暫存器置零,彷彿與自身 XOR 或減去自身: XOR , SUB , PXOR , XORPS , XORPD 以及 PSUBxxx 與 PCMPGTxx 的各個版本。
帶有 v 字首的指令行為相同。不使用執行單元,吞吐率是每時鐘週期 4 個置零操作。
這對所有 32 位及 64 位通用暫存器以及所有 128 位與 256 位向量暫存器都奏效。它不能作用於 8 位與 16 位暫存器,因為僅暫存器部分被置零。它部分作用於 64 位 mmx 暫存器:暫存器被置零,無需等待之前的值,但它使用執行單元(以解決作為浮點棧暫存器與 mmx 暫存器的雙重使用)。
PCMPEQxx 指令的所有版本可以將一個暫存器置為全 1 ,無需等待該暫存器的之前值。不過,它要使用一個執行單元。
以下指令沒有兩個輸入運算元是同一個暫存器的特殊情形: CMP , SBB , ANDN , PANDN , ANDNPS , ANDNPD , CMPEQPS , CMPEQPD 。
無需執行單元的指令
上述通過,比如 XOR EAX, EAX 指令,將暫存器置零的特殊情形,在暫存器分配階段處理,無需使用任何執行單元。
少數其他指令也無需使用任何執行單元來處理。這些是 CLC , FXCH , NOP (包括長 nop ),但不包括 FNOP 。
移動指令的消除
大多數暫存器暫存器移動在暫存器分配階段,以與 Ivy Bridge 相同的方式消除,如 113 頁所述。
超過 80% 的可能情形中,大多數消除在通常會成功。串接的移動也可被消除。
所有 32 位及 64 位通用暫存器以及所有 128 位與 256 位向量暫存器都可能消除移動。 8 位與 16 位暫存器不可能, 64 位 mmx 暫存器也不可能。
不像 Ivy Bridge , Haswell 與 Broadwell 不能消除零擴充套件移動。但隱含的零擴充套件移動可以消除,如 MOV EAX, EBX (零擴充套件到 RAX ),及 VMOVAPS XMM0, XMM1 (零擴充套件到 YMM0 )。
到自身的移動永遠不會被消除。例如 mov eax, eax 不會被消除。
一個消除的移動有 0 時延,不使用任何執行單元。但確實消耗解碼器的頻寬。
10.9. 執行單元
處理器有若干訪問 8 個執行埠的執行單元。這給出了理論上執行單元裡每時鐘週期 8 個 μop的最大吞吐率。不過,整個設計的典型吞吐率是每時鐘週期4條指令。因此,即使使用μop融合,在臨時爆發以外,保持所有執行埠繁忙,是不可能的。
許多執行單元是有備份的,因此對一個特定的μop找到一個空閒的單元可能性總是很大。有4個整數ALU,因此最常見的整數操作可以每時鐘週期4條指令的吞吐率執行。有3個埠可以處理整數向量操作。兩個埠可以處理浮點向量操作。兩個埠可以處理分支。兩個埠可以處理記憶體讀操作,一個埠可以處理記憶體寫。
很奇怪,僅有一個埠用於浮點加法,但兩個埠用於浮點乘法。
8 個埠及它們的常見操作在表 10.1 列出。
埠 |
操作 |
時延 |
0 |
整數與向量算術、邏輯、偏移 |
1 |
0 |
浮點乘法 |
Haswell : 5 , Broadwell : 3 |
0 |
浮點 FMA 與長雙精度乘法 |
5 |
0 |
整數向量乘法 |
5 |
0 |
除法 |
可變 |
0 |
分支 |
1 – 2 |
1 |
整數與向量算術、邏輯 |
1 |
1 |
浮點加法 |
3 |
1 |
整數乘法 |
3 |
1 |
浮點乘法 |
Haswell : 5 , Broadwell : 3 |
1 |
浮點 FMA |
5 |
2 |
載入,地址生成 |
|
3 |
載入,地址生成 |
|
4 |
寫 |
|
5 |
整數與向量算術、邏輯 |
1 |
5 |
向量組合( permute ) |
1 (如果跨通道, 3 ) |
5 |
AES 加密 |
7 |
6 |
整數算術、邏輯、偏移 |
1 |
6 |
分支 |
1 - 2 |
7 |
寫地址生成 |
表10.1. Haswell與Broadwell中的執行單元
所有向量執行單元有完整的256位能力,除了除法、平方根及加密。一個256位單元不能分解並同時用於2個128位操作。
整數向量操作的時延與通用暫存器中的操作相同。這使得在用完通用暫存器時,將XMM暫存器用於整數操作成為可能。雖然,更少的執行單元支援向量操作。
融合乘法與加法
Haswell與Broadwell有兩個可以處理型別為a = b * c + d融合乘加(FMA)指令的執行單元。
一條FMA指令使用單條指令與單個μop執行一個乘法與一個加法或減法。這可以提升乘法與加法組合經常出現的浮點程式碼的效能。吞吐率是每時鐘週期兩條FMA指令,時延為5。
Intel一開始設計FMA指令帶有4個運算元(FMA4),但後來改變計劃為設計帶有3個運算元(FMA3),其中重用其中一個輸入暫存器為輸出暫存器是必須的,即a = b * c + a。
一個μop 可以有多少輸入依賴?
所有之前有亂序執行能力的Intel處理器,有μop不能有超過2個輸入依賴的設計侷限。我從所有具有超過兩個輸入依賴的指令都被分解為至少兩個μop的事實,得出這個結論。最常見的例子有:
; Example 10.1. Instructions with three input dependencies
mov [eax+ebx],e cx ; depends on 3 registers
adc eax, ebx ; depends on 2 registers and carry flag
cmovz eax,e bx ; depends on 2 registers and zero flag
pblendvb xmm1, xmm2, xmm0 ; depends on 3 registers
在Haswell及所有之前的Intel處理器上,這些指令都被分解為兩個μop。在Haswell中,融合乘加指令的引入,使得消除μop兩個輸入依賴的限制變得必要。因此,FMA指令是Intel處理器上第一批使用超過兩個輸入依賴的μop的指令。可能需要重新設計亂序機制以允許帶有3個輸入依賴的μop。
通過對進位加法,借位減法,及通用暫存器上的條件移動,使用帶3個輸入依賴的μop,Broadwell稍微更進一步地採取了這個重新設計。在Broadwell上,所有的記憶體寫與混合指令仍然分解為兩個μop。
讀寫頻寬
有兩個相同的記憶體讀埠(埠2與3)及一個寫埠(埠4)。這些埠都被擴充套件到256位。這使得每時鐘週期,與任何最多256位暫存器的兩次記憶體讀及一個記憶體寫,成為可能。所有寫操作需要在埠2,3或7上的地址計算。
資料旁路時延
如第99頁所示,執行單元被分為域,當整數域中一條指令的輸出用作浮點域中一條指令的輸入時,有時有一個時鐘週期的時延,反之亦然。例如:
; Example 10.2. Data bypass delays
addps xmm0, xmm1
por xmm0, xmm2 ; 1 clock delay
mulps xmm0, xmm3 ; 1 clock delay
可以通過更合適的ORPS替換POR指令,避免例子10.2裡的時延。
不過,在Haswell與Broadwell上,這樣的時延更少。在以下情形裡,我沒有找到這樣的時延:
- 當一條浮點布林指令,比如ORPS,與整數資料一起使用時。
- 在使用一個錯誤型別的移動指令時,如MOVPS或MOVDQA。
- 在使用一個錯誤型別的混排指令時,如SHUFPS或PFHUFD。
不過,在一條浮點混合指令用於整數資料時,如BLENDPS,我確實找到了時延。
像MOVD這樣在通用暫存器與向量暫存器間移動資料的指令,僅有1時鐘週期時延且沒有額外的旁路時延。類似的,從向量暫存器移動資料到整數標記暫存器的指令,比如COMISS與PTEST沒有額外的旁路時延。
256 位向量
所有向量執行單元有完全的256位吞吐率,除了除法、平方根與加密。
用於YMM暫存器上所有操作的256位資料通道被分為2個128位通道(lane)。所有在這2個通道間移動資料的指令都有3時鐘週期的時延,其他移動指令僅有1時鐘週期的時延,例如:
; Example 10.3. Moving data between 128-bit lanes
vextracti128 xmm0,ymm1,1 ; move from upper to lower lane, 3 clocks
vextracti128 xmm0,ymm1,0 ; move in lover lane only, still 3 clocks
vmovdqa xmm0,xmm1 ; same operation in 0 - 1 clocks
在例子10.3中第二條指令有3時鐘週期的時延,即使所有資料待在較低的通道,因為VEXTRACTI128指令有通道間移動資料的可能性。可以通過使用VMODDQA指令來避免這個時延,在這個情形裡它做相同的事情,但不會跨通道移動資料。
如章節9.12 所述,混用256位VEX程式碼與128位非VEX程式碼有嚴重的懲罰。所有在狀態B(修改)與狀態C(儲存)間的轉換有70時鐘週期的時延。這個時延在32位模式與64位模式裡是相同的,即使儲存的暫存器數不一樣。可以通過將所有非VEX向量指令替換為V字首版本,或者在一條包含256位向量程式碼的函式呼叫另一個可能使用非VEX程式碼的函式,或返回到可能使用非VEX程式碼的一個函式時,釋出一條VZEROUPPER指令,來避免這個時延。
256位暫存器的兩個半部不視為無關的,除了非期望的狀態C(儲存),如章節9.12所述。將256位執行單元分解為兩個128位單元來同時執行兩個128位操作是不可能的。
混用不同時延的μop
某些更舊的處理器,在具有不同時延的μop釋出到同一個執行埠時,有一個回寫衝突,如第頁所述。在Haswell與Broadwell上,我沒有找到這樣問題的證據。部分通過標準化時延,如表10.1所示,部分通過多個回寫通道,這個問題被解決了。
下溢與次正規數(subnormal )
當浮點操作接近下溢時,出現次正規數。在某些情形裡,次正規數的處理代價非常高,因為次正規結果由微程式碼異常處理的。
在一個正規值上操作給出一個次正規結果的所有情形裡,Haswell與Broadwell有大約124個時鐘週期的懲罰。正規值與次正規值間的乘法有一個類似的懲罰,不管結果是否正規。正規值與次正規值相加沒有懲罰,不管結果如何。上溢、下溢、無窮或非值結果沒有懲罰。
如果在MXCSR暫存器裡,同時設定了“flush-to-zero”與“denormals-are-zero”模式,次正規值的懲罰可以避免。