簡體   English   中英

為什么緩存讀取未命中比寫入未命中快?

[英]Why cache read miss is faster than write miss?

我需要使用另一個數組(readArray)計算一個數組(writeArray),但是問題是數組之間的索引映射不同(必須使用readArray的索引y的值來計算writeArray的索引x的值)緩存友好。

但是,我可以選擇循環是順序瀏覽readArray還是順序瀏覽writeArray。

所以這是一個簡化的代碼:

int *readArray = new int[ARRAY_SIZE];       // Array to read
int *writeArray = new int[ARRAY_SIZE];      // Array to write
int *refArray = new int[ARRAY_SIZE];        // Index mapping between read and write, could be also array of pointers instead indexes

// Code not showed here : Initialization of readArray with values, writeArray with zeroes and refArray with random indexes for mapping between readArray and writeArray (values of indexes between 0 and ARRAY_SIZE - 1)

// Version 1: Random read (browse writeArray/refArray sequentially)
for (int n = 0; n < ARRAY_SIZE; ++n) {
    writeArray[n] = readArray[refArray[n]];
}

// Version 2: Random write (browse readArray/refArray sequentially)
for (int n = 0; n < ARRAY_SIZE; ++n) {
    writeArray[refArray[n]] = readArray[n];
}

我當時以為高速緩存讀未命中要比寫未命中慢(因為如果下一條指令依賴於讀數據,那么CPU需要等待讀取完成才能等待完成,但是寫入時它不需要等待處理下一條指令),但是對它進行了性能分析似乎版本1比版本2快(版本2比版本1慢50%)。

我也嘗試過這個:

// Version 3: Same as version 2 but without polluting cache
for (int n = 0; n < ARRAY_SIZE; ++n) {
    _mm_stream_si32(&writeArray[refArray[n]], readArray[n]);
}

因為我不需要讀取writeArray的值,所以沒有理由用寫入的值來污染緩存,但是此版本比其他版本更慢(比版本1慢6700%)。

為什么寫未讀比讀未讀慢? 為什么即使我們之后不讀取這些寫入的數據,繞過緩存進行寫入也比使用緩存慢呢?

讓我們從最后一個版本開始-您所做的是將流存儲用於非順序(非流)訪問模式。 您正在隨機訪問整數,這意味着您正在對完整的緩存行進行部分寫入(整數大小)。 在普通寫入中,這無關緊要,因為內核將這條線拉到緩存中,並且僅修改了必要的塊(稍后將在需要存儲其他內容時將其寫回),但是由於您要求為了避免緩存,您實際上必須在內存中進行部分合並,這是非常昂貴且阻塞的。 僅當確保您修改整行(例如,通過順序遍歷數組)時,流存儲才有用。

至於第二版-您的假設是正確的,如果通過加載存在數據依賴關系,您將不得不等待它們,但是這里沒有真正的依賴關系鏈。 您只有一組具有2級依賴關系的負載,但是它們之間沒有相互依賴關系,從而不會導致迭代之間的任何序列化(即,甚至在n == 1完成第一個負載之前,迭代n == 2和n == 3可能會開始)。 實際上,假設您的CPU可以承受N個未完成的訪問(取決於所涉及的大小和緩存級別),您將並行啟動對refArray的前N個引用(假設索引計算很快),然后對readArray的前N個引用readArray ,然后下一批,依此類推。

現在,由於不存在數據依賴性,因此成為帶寬問題。 通常,在這種情況下,由於處理器的亂序性質,使加載變得容易得多-一旦知道地址(僅取決於快速索引計算),就可以並行和亂序啟動它們。 。 另一方面,需要按程序順序觀察存儲(以保持內存一致性),這幾乎會將它們序列化(那里有一些可能的CPU技巧,具體取決於您的微體系結構,但不會改變大小)圖片)。

編輯:版本2中添加的另一個約束(我認為這更為關鍵)是內存歧義消除。 處理器必須計算負載並存儲地址,以便知道是否存在任何沖突(我們知道沒有沖突,但是處理器沒有...)。 如果加載取決於存儲,則必須將其阻止,以防必須轉發新數據。 現在,由於盡早在OOO機器中加載了貨物,因此至關重要的是,盡早知道所有商店的地址,以避免發生碰撞(或更糟的是-推測失敗並導致大量沖洗)

暫無
暫無

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

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