簡體   English   中英

現代x86實現能否從多個以前的存儲中進行存儲?

[英]Can modern x86 implementations store-forward from more than one prior store?

如果負載與兩個較早的存儲區重疊(並且該負載未完全包含在最舊的存儲區中),現代的Intel或AMD x86實現是否可以從兩個存儲區轉發來滿足負載?

例如,考慮以下順序:

mov [rdx + 0], eax
mov [rdx + 2], eax
mov ax, [rdx + 1]

最終的2字節加載從緊接的前一個存儲中獲取其第二個字節,但在此之前從存儲中獲取第一個字節。 可以將此存儲轉發給存儲,還是需要等到兩個先前的存儲都提交到L1時才能進行?

請注意,通過此處的存儲轉發 ,我將包括可以滿足仍在存儲緩沖區中的存儲讀取的任何機制,而不是等待它們提交到L1的機制,即使這是比“從A轉發”的最佳情況慢的路徑。單店”案例。

沒有。

至少不是在Haswell,Broadwell或Skylake處理器上。 在其他Intel處理器上,限制是相似的(Sandy Bridge,Ivy Bridge)或更嚴格的限制(Nehalem,Westmere,Pentium Pro / II / II / 4)。 在AMD上,適用類似的限制。

來自Agner Fog出色的優化手冊

Haswell / Broadwell

英特爾和AMD CPU的微體系結構

§10.12商店轉運攤位

在某些條件下,處理器可以將內存寫入轉發給來自同一地址的后續讀取。 在以下情況下,商店轉發有效:

  • 寫入64位或更少后,隨后進行大小和地址相同的讀取,無論對齊方式如何。
  • 寫入128或256位后,再進行相同大小和相同地址的讀取,完全對齊。
  • 當寫入64位或更少的位時,隨后進行較小大小的讀取,該大小將完全包含在寫入地址范圍內,而與對齊方式無關。
  • 當任何大小的對齊寫入之后,是兩個一半的讀取兩次或四個季度的四個讀取等,其自然對齊在寫入地址范圍內。
  • 當對齊的128位或256位寫入之后是64位或更少的讀取,且不會越過8字節邊界。

如果存儲塊越過64字節的高速緩存行邊界,則會發生2個時鍾的延遲。 如果所有數據都具有自然對齊方式,則可以避免這種情況。

在以下情況下,商店轉發失敗:

  • 當任何大小的寫入后跟着較大的讀取
  • 當任何大小的寫入后跟部分重疊的讀取
  • 當寫入128位之后是較小的讀取時,越過兩個64位半部分之間的邊界
  • 當寫入256位之后是讀取128位時,越過兩個128位半部之間的邊界
  • 當寫入256位之后是讀取64位以下的字符時,越過四個64位四分之一之間的任何邊界

失敗的商店轉發比成功的商店轉發花費10個時鍾周期。 在寫入未對齊至少16位的128或256位之后,代價要高得多-大約50個時鍾周期。

重點已添加

天空湖

英特爾和AMD CPU的微體系結構

第11.12條商店轉運攤位

在某些情況下,Skylake處理器可以將內存寫入轉發給來自同一地址的后續讀取。 存儲轉發比以前的處理器快一個時鍾周期。 對於32或64位的操作數,在最佳情況下,寫存儲器后再從同一地址進行讀取需要4個時鍾周期,而對於其他操作數大小,則需要5個時鍾周期。

當128或256位的操作數未對齊時,存儲轉發將增加多達3個時鍾周期的損失。

當任何大小的操作數越過高速緩存行邊界(即可被64字節整除的地址)時,存儲轉發通常會額外花費4-5個時鍾周期。

寫入后再從相同地址進行較小的讀取幾乎不會造成任何損失。

當讀偏移量但完全包含在寫所覆蓋的地址范圍內時,寫64位或更少的位,然后進行較小的讀,將產生1-3個時鍾的損失。

對齊寫入128或256位,然后讀取兩個半部或四個四分之一等中的一個或兩個,等等,幾乎沒有損失。 不適合兩半或四分之一的部分讀取可能會額外花費11個時鍾周期。

大於寫入的讀取 ,或覆蓋已寫入和未寫入字節的讀取,大約需要11個時鍾周期。

重點已添加

一般來說:

Agner Fog的文檔指出,跨微體系結構的一個共同點是,如果對齊寫入並且讀取的值是寫入值的一半四分之一 ,則存儲轉發的可能性更大。

一個測試

具有以下緊密循環的測試:

mov [rsp-16], eax
mov [rsp-12], ebx
mov ecx, [rsp-15]

顯示ld_blocks.store_forward PMU計數器確實確實增加了。 該事件記錄如下:

ld_blocks.store_forward [此事件計算加載操作獲得阻止存儲轉發的真正的存儲區阻止代碼的次數。 這包括以下情況:-先前存儲與負載沖突(重疊不完全)

  • 由於u-arch限制,無法進行商店轉發

  • 先前的鎖定RMW操作不會轉發

  • 存儲設置了無轉發位(不可緩存/頁面拆分/屏蔽的存儲)

  • 使用所有阻塞存儲(主要是圍欄和端口I / O)

這表明當只讀僅部分重疊最近的較早存儲時(即使考慮了較早的存儲也完全包含了該存儲),存儲轉發確實會失敗。

有序的Atom可能能夠執行此存儲轉發而完全不會停止。

Agner Fog沒有特別針對Atom提到這種情況,但是與所有其他CPU不同,它可以以1c的延遲將存儲從存儲轉發到更大或更不同的負載。 Agner發現的唯一例外是在高速緩存行邊界上,其中Atom太可怕了(即使不涉及存儲轉發,CL拆分加載或存儲也要付出16個周期的懲罰)。


可以將此存儲轉發給存儲,還是需要等到兩個先前的存儲都提交到L1時才能進行?

這里有一個術語問題。 如@IWill的答案中所列出的,許多人會把“是否可以將此存儲轉發給存儲”解釋為,是否可以以與滿足快速路徑存儲轉發的所有要求一樣低的延遲發生。 (如果所有加載的數據都來自最近的存儲,以與任何加載重疊,並且滿足其他相對/絕對對齊規則)。

起初我以為您錯過了第三種可能性,即較慢但仍(幾乎?)固定延遲轉發而不等待提交給L1D的可能性,例如,在某些情況下采用了一種刮除整個存儲緩沖區(可能是L1D的負載)的機制。 Agner Fog和Intel的優化手冊稱“商店轉發失敗”。

但是現在我看到這個措詞是故意的,您確實想問第三個選項是否存在。

您可能需要將其中一些內容修改為您的問題。 總之,英特爾x86 CPU的三個可能的選擇是:

  1. 英特爾/阿格納(Intel / Agner)對存儲轉發成功的定義,其中所有數據僅來自最近一家具有低延遲( 幾乎)固定延遲的商店。
  2. 掃描整個存儲緩沖區並組裝正確的字節(根據程序順序)需要額外(但有限)的延遲,並且(如果有必要或始終是?)從L1D加載以提供最近未存儲的任何字節的數據。

    這是我們不確定存在的選項

    它還必須等待尚未准備好輸入的存儲數據塊中的所有數據,因為它必須遵守程序順序。 可能會發布一些有關具有未知存儲地址的推測執行的信息(例如,猜測它們沒有重疊),但是我忘記了。

  3. 等待所有重疊的存儲提交到L1D,然后從L1D加載。

    在某些情況下,某些真正的x86 CPU可能會退一步,但它們可能始終使用選項2,而不會引入StoreLoad障礙。 (請記住,x86存儲必須按程序順序進行提交,並且加載必須按程序順序進行。這將有效地耗盡存儲緩沖區到這一點,例如mfence ,盡管以后對其他地址的加載仍可以推測性地進行存儲轉發或僅采用來自L1D的數據。)


中間選項的證據:

x86中提出的鎖定方案是否可以對一個狹窄的商店重新排序,使其具有更大的負載並完全包含它? 如果存儲轉發失敗需要刷新到L1D,它將正常工作。 由於沒有mfence ,它不能在實際的硬件上mfence ,因此有力的證據表明,真正的x86 CPU正在將存儲緩沖區中的數據與L1D中的數據合並。 因此,選項2存在並在這種情況下使用。

另請參閱Linus Torvalds對x86確實允許這種重新排序的解釋 ,以回應提出與該SO問題相同的鎖定思想的其他人。

我尚未測試存儲轉發失敗/停頓處罰是否可變,但是如果不是最好的話,則強烈暗示當最佳情況轉發不起作用時,它會退回到檢查整個存儲緩沖區。

希望有人能回答在x86上存儲到裝載轉發失敗的代價是什么? ,正是這樣問。 我會解決的。

Agner Fog只提到了一個單一的數字來表示存儲轉發的罰款,並且沒有說如果高速緩存未命中的存儲比未轉發的存儲要早的話會更大。 (這將導致很大的延遲,因為由於x86的順序內存強,存儲必須按順序提交到L1D。)他也沒有說任何關於數據來自1個存儲+ L1D與來自1個存儲的不同情況。兩個或兩個以上商店的一部分,因此我猜想它在這種情況下也適用。


我懷疑“失敗”的存儲轉發非常普遍,以至於晶體管值得處理的速度要比刷新存儲隊列和從L1D重新加載更快。

例如, gcc並沒有專門嘗試避免存儲轉發停頓 ,它的某些慣用法會導致它們(例如__m128i v = _mm_set_epi64x(a, b);在32位代碼中已經存儲/重載到堆棧中了)大多數情況下,大多數CPU上的策略錯誤,因此會報告該錯誤)。 效果不好,但結果通常不會帶來災難性的AFAIK。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM