1. 程式人生 > >Intel, AMD及VIA CPU的微架構(27)

Intel, AMD及VIA CPU的微架構(27)

9.11. 暫存器的部分訪問

一個通用暫存器的不同部分可以儲存在不同的臨時暫存器中,以消除偽依賴性。一個暫存器部分寫後跟一個整個暫存器讀時,出現一個問題:

; Example 9.4. Partial register problem

mov al, 1

mov ebx, eax

通過插入一個額外的μop來合併暫存器的不同部分,在Sandy Bridge中解決了這個問題。我假定這個額外的μop在ROB-讀階段生成。在上面的例子中,ROB-讀將生成一個額外的μop,在MOV EBX, EAX指令前,將AL與EAX餘下部分合併為一個臨時暫存器。寫一個部分暫存器沒有懲罰,除非稍後讀同一個暫存器更大的部分。

Ivy Bridge僅在高8位暫存器(AH,BH,CH,DH)被修改時插入一個額外的μop,在像例子9.4的情形裡不會。

標記的部分訪問暫停

Sandy Bridge與Ivy Bridge不僅對通用暫存器,還對標記暫存器使用一個額外μop來整合部分暫存器的方法,不像之前的處理器僅對通用暫存器使用這個方法。這發生在寫標記暫存器一部分,後跟讀該標記暫存器更大一部分時。因此,之前處理器的部分標記暫存器暫停(參考第89頁)被一個額外的μop所替代。在一條旋轉指令後讀標記暫存器時,Sandy Bridge也產生一個額外的μop,Ivy Bridge不會。

向量暫存器的部分訪問

在重排緩衝中,一個XMM暫存器不會分解為部分。因此,無需額外μop,在寫一個XMM暫存器的部分時,也沒有部分訪問暫停。但寫一個向量暫存器部分有對暫存器之前值的一個依賴。參考第103頁的例子8.8。

在VEX指令裡,YMM暫存器的兩個半部總是視為相關的,但在VEX與非VEX模式間切換時,兩個半部可以分開,如下所述。

9.12. VEX與非VEX模式間的轉換

AVX指令集定義了三個處理器模式,如手冊2“優化彙編例程”,章節13.6“使用AVX指令集與YMM暫存器”描述的那樣。這三個狀態是:

  1. (乾淨狀態)YMM暫存器高半部沒有使用,且已在為零。
  2. (修改狀態)至少一個YMM暫存器的高半部被使用,且包含資料。
  3. (儲存狀態)所有YMM暫存器被分為兩部分。低半部由XMM指令使用,高半部不變。所有高半部都儲存在一個暫存器(scratchpad)中。如果轉換到狀態B需要,每個暫存器的兩部分將被再次接合起來。

狀態C是一個不期望的狀態。在混合使用YMM暫存器的程式碼與使用XMM暫存器的非VEX指令程式碼時出現。BàC,CàB及CàA的轉換,根據我的測量,在Sandy Bridge上,每個大約需要70個時鐘週期。AàC及BàA的轉換需要零或一個時鐘週期。最好不混用VEX與非VEX程式碼,以及在任何使用VEX編碼指令的程式碼序列後插入一條VZEROUPPER指令,避免至/自狀態C的慢速轉換。

9.13. 快取與記憶體訪問

快取

Sandy BridgeIvy Bridge

μop快取

每核1536 μop8路,每行6 μop

1級程式碼

每核32 kB8路,每行64 B,時延4

1級資料

每核32 kB8路,每行64 B,時延4

2

每核256 kB8路,每行64 B,時延11

3

最多16 MB12路,每行64 B,時延28,共享

表9.5. Sandy Bridge上的快取大小

每個核上有一個快取,除了最後一級快取。在一個核可以執行兩個執行緒時,所有的快取線上程間共享。將來可能有更多版本具有不同的最後一級快取大小。

快取庫衝突

在資料快取中,每個連續的128位元組,或兩個快取行,被分為8個庫,每個16位元組。如果兩個記憶體地址有相同的庫號碼,即兩個地址的4 – 6位元位相同,不可能在同一時鐘週期裡執行這兩個讀。例如:

; Example 9.5. Sandy bridge cache bank conflict

mov eax, [rsi]                  ; Use bank 0, assuming rsi is divisible by 40H

mov ebx, [rsi+100H]      ; Use bank 0. Cache bank conflict

mov ecx, [rsi+110H]       ; Use bank 1. No cache bank conflict

另外,在具有相同組與偏移的記憶體地址間,即相距4K位元組的倍數,存在一個偽依賴:

; Example 9.6. Sandy bridge false memory dependence

mov [rsi], eax

mov ebx, [rsi+1000H] ; False memory dependence

未對齊的記憶體訪問

對運算元大小是64位或更小的非對齊記憶體訪問,幾乎沒有懲罰,除非使用多個快取庫。

指令預取

Ivy Bridge預取指令有一個問題。看起來Ivy Bridge浪費時間在預取已經在快取中的資料。在Ivy Bridge上,從同一地址重複預取的測量吞吐率是每43時鐘週期一次預取,而在Sandy Bridge上是每時鐘週期兩條預取指令的吞吐率。

9.14. ​​​​​​​寫轉發暫停

在特定條件下,處理器可以將一個記憶體寫轉發給相同地址的一個後續讀。相比之前的處理器,這個寫轉發可以在更多的情形下工作,包括非對齊的情形。寫轉發在以下情形工作:

  • 一個64位或更小的寫,後跟一個相同大小與相同地址的讀時,不管是否對齊。
  • 一個128256位寫,後跟一個相同大小與相同地址的讀時,16位元組邊界對齊。
  • 一個64位或更小的寫,後跟一個更小的完全包含在寫地址範圍裡時,不管是否對齊。
  • 一個任意大小的對齊寫,後跟兩部分的兩個讀,或四部分的四個讀等,在寫地址範圍裡自然對齊時。
  • 一個128位或256位對齊寫,後跟一個64位或更小的、沒有跨過8位元組邊界的讀時。

64位或更小的寫轉發,跨越快取行邊界沒有懲罰。如果讀的運算元比之前的寫要大,或者與寫地址部分重疊,寫轉發不能工作:

; Example 9.7. Failed store forwarding when read bigger than write

mov dword ptr [esi], eax               ; Write lower 4 bytes

mov dword ptr [esi+4], edx          ; Write upper 4 bytes

movq xmm0, qword ptr [esi]        ; Read 8 bytes. Stall

在大多數情形裡,對失敗寫轉發的懲罰是大約12個時鐘週期。

在寫沒有對齊到至少16位元組邊界時,對128位或256位寫轉發,懲罰會異常地大。在這個情形裡,我測得在Ivy Bridge上,16位元組讀/寫大約是50時鐘週期,32位元組讀/寫大約是210時鐘週期。

9.15. 多執行緒

Sandy BridgeIvy Bridge的某些版本在每個核上可以執行兩個執行緒。這意味著每個執行緒僅得到一半資源。資源以下面的方式在執行在同一個核上的兩個執行緒間共享:

快取:所有的快取資源完全線上程間共享。一個執行緒用得越多,另一個可以用的越少。

分支目標緩衝與分支歷史模式表:這些線上程間完全共享。

指令獲取與解碼:指令獲取器與解碼器在兩個執行緒間平均共享,每個執行緒隔一個時鐘週期使用它們。

迴圈緩衝:每個執行緒一個迴圈緩衝。

暫存器分配與重新命名資源平均共享,每個執行緒隔一個時鐘週期使用它們。

重排緩衝與保留站。這些完全共享。

執行埠與執行單元。這些完全共享。一個執行緒可以使用一個執行埠,而另一個執行緒使用另一個埠。

讀寫緩衝:這些完全共享。

永久暫存器檔案:每執行緒一個。

顯然,如果任一共享資源是效能的限制因素,每核執行兩個執行緒沒有好處。不過,在許多情形裡,執行資源超出單個執行緒所需。在大量時間花在快取不命中與分支誤預測的情形裡,每個核執行兩個執行緒特別有優勢。不過,任一共享資源是瓶頸時,每核執行兩個執行緒沒有優勢。相反,每個執行緒很可能執行得比單個執行緒一半還慢,因為快取與分支目標緩衝中的逐出,以及其他資源衝突。在CPU中沒有辦法給一個執行緒比另一個執行緒更高的優先順序。

    1. 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位向量操作。執行時延僅在長的依賴鏈中是關鍵的。

部分暫存器訪問

在寫暫存器一部分後,讀整個暫存器有一個懲罰。使用MOVZXMOVSX8位或16位記憶體運算元讀入一個32位暫存器,而不是使用暫存器更小的部分。

回收

在我的所有測試中,都沒有觀察到μop的回收成為一個瓶頸。

分支預測

分支預測器沒有專門識別迴圈。帶有許多分支迴圈的預測遜色於之前的處理器。分支歷史模式表與分支緩衝可能比之前的處理器大,但誤預測仍然常見。對適合μop快取的程式碼,誤預測懲罰要更小。

記憶體訪問

Sandy Bridge有兩個記憶體讀埠,而之前的處理器僅有一個。這是一個重要的改進。在最大記憶體頻寬利用率時,快取庫衝突相當常見。

Ivy Bridge預取指令有一個嚴重的問題,它極慢。

多執行緒

大多數關鍵資源線上程間共享。這意味著在多執行緒應用程式中,瓶頸變得更加關鍵。

文獻

"Intel 64 and IA-32 Architectures Optimization Reference Manual". Intel 2011.