2014-02-16 47 views
5

我有兩個數組:AN_A隨機整數和BN_B0(N_A - 1)之間的隨機整數。我用的號碼B作爲索引到A在下面的循環:硬件預取器在這種內存訪問模式中是否受益?

for(i = 0; i < N_B; i++) { 
    sum += A[B[i]]; 
} 

進行實驗上的英特爾i7-3770,N_A = 256萬美元,N_B = 64萬美元,該環路僅需要0.62秒,其對應到大約9納秒的存儲器訪問延遲。

由於此延遲太小,我想知道硬件預取器是否正在發揮作用。有人可以提供解釋嗎?

回答

2

CPU在指令流中提前收費,並將一次處理多個未完成的負載。該流如下所示:

load b[0] 
load a[b[0]] 
add 
loop code 

load b[1] 
load a[b[1]] 
add 
loop code 

load b[1] 
load a[b[1]] 
add 
loop code 

... 

迭代僅由快速運行的循環代碼序列化。 所有加載可以同時運行。 Concurrency is just limited by how many loads the CPU can handle.

我懷疑你想要測試隨機,不可預測的序列化內存負載。現代CPU實際上很難實現。嘗試引入不可分割的依賴關係鏈:

int lastLoad = 0; 
for(i = 0; i < N_B; i++) { 
    var load = A[B[i] + (lastLoad & 1)]; //be sure to make A one element bigger 
    sum += load; 
    lastLoad = load; 
} 

這需要執行最後一次加載,直到可以計算下一個加載的地址。

4

HW預取程序可以看穿您的第一級間接尋址(B[i]),因爲這些元素是連續的。它能夠提前發出多個預取,所以你可以假設到B的平均訪問會碰到緩存(L1或L2)。但是,預取程序無法預測隨機地址(存儲在B中的數據)並從A中預取正確的元素。在幾乎所有對A的訪問中都必須執行內存訪問(忽略由於重用而偶爾出現的幸運緩存命中)

您看到如此低的延遲的原因是對A的訪問是非序列化的,CPU可以同時訪問A的多個元素,所以時間不會累積。實際上,您在這裏測量內存帶寬,檢查訪問整個64M元素需要多長時間,而不是內存延遲(訪問單個元素需要多長時間)。

CPU內存單元的合理「快照」應該顯示幾個未完成的請求 - 對B[i]B[i+64],...的幾次訪問(中間訪問只需在每個請求獲取64字節行時簡單合併),全部這可能是反映未來值i的預取,根據之前提取的B的元素隨機訪問A元素。

要衡量延遲,您需要每次訪問都取決於前一個結果,例如,通過使A中的每個元素的內容成爲下一個訪問的索引。

相關問題