我無意中發現沒有一次進入「非一致」,並在相關的圖形programming.I一直在尋找一個簡單而明確的解釋什麼是GPU上的連貫內存?的
tech papers「相干」記憶,卻發現大多是「骨灰級」的論文this類型。我很樂意接受外行人的風格,回答關於GPU架構上的連貫內存,以及它與其他(可能不是連貫的)內存類型的比較。
我無意中發現沒有一次進入「非一致」,並在相關的圖形programming.I一直在尋找一個簡單而明確的解釋什麼是GPU上的連貫內存?的
tech papers「相干」記憶,卻發現大多是「骨灰級」的論文this類型。我很樂意接受外行人的風格,回答關於GPU架構上的連貫內存,以及它與其他(可能不是連貫的)內存類型的比較。
內存是內存。但不同的東西可以訪問這些內存。 GPU可以訪問內存,CPU可以訪問內存,也可以訪問其他硬件位,無論如何。
如果其他人對該內存所做的更改是可見,則讀者可以通過「連貫」訪問內存。現在,你可能會認爲這是愚蠢的。畢竟,如果內存改變了,那麼可能怎麼可能無法看到它?
簡單地說,緩存。
事實證明,改變內存是昂貴的。所以我們盡一切可能去避免改變內存,除非我們絕對要。當您將一個字節從CPU寫入內存中的指針時,CPU不會寫入該字節。或者至少,不要回憶。它將其寫入稱爲「緩存」的內存的本地副本。
原因是,一般來說,應用程序不會寫入(或讀取)單個字節。它們更可能以小塊形式(並讀取)大量字節。因此,如果您要執行像內存加載或存儲這樣的昂貴操作,則應加載或存儲大量內存。因此,您將對緩存中的大塊內存所做的所有更改進行存儲,然後在將來的某個時刻將該緩存塊寫入實際內存。
但是,如果您有兩個使用相同內存的獨立設備,則需要某種方式來確保寫入一個設備使其他設備可見。大多數GPU不能讀取CPU緩存。而且大多數CPU語言沒有語言級別的支持來說「嗨,我寫給內存的東西?我真的認爲你現在就把它寫到內存中。」所以你通常需要一些東西來確保變化的可見性。這是標有「HOST_COHERENT」
在福爾康,內存意味着,如果您寫入到內存(通過映射的指示器,因爲這是唯一的福爾康,您可以直接寫入到內存的方式),你不需要使用特殊功能來確保GPU可以看到這些變化。 GPU保證所有更改的可見性。如果該標誌在內存中不可用,則必須使用Vulkan API來確保要訪問的特定數據區域的一致性。
對於一致的內存,硬件方面有兩件事情之一。對內存的CPU訪問不會緩存在任何CPU的緩存中,或者GPU可以直接訪問CPU的緩存(可能是因爲與CPU相同)。您通常可以知道後者正在發生,因爲Vulkan的片上GPU實現不會提供非連貫的內存選項。
如果內存一致,那麼所有訪問該內存的線程都必須始終同意內存的狀態,例如:如果線程0讀取內存位置A並且線程1同時讀取相同的位置,則兩個線程都應該總是讀取相同的值。
但是,如果內存不一致,那麼線程A和B可能會讀回不同的值。線程0可以認爲位置A包含1,而線程認爲該位置包含2.不同的線程會對內存有不連貫的看法。
核心數量衆多時,很難實現一致性。通常每個核心都必須知道來自所有其他核心的內存訪問。因此,如果四核CPU中有4個內核,則一致性並不難實現,因爲每個內核都必須知道其他3個內核的內存訪問地址,但是在具有16個內核的GPU中,必須讓每個內核知道內存訪問15個其他內核。核心使用所謂的「緩存一致性協議」交換關於其緩存內容的數據。
這就是爲什麼GPU通常只支持有限形式的一致性。如果某些內存位置是隻讀的或者只能由單個線程訪問,則不需要一致性。如果緩存很小,並不總是需要一致性,但只能在程序的特定指令下使用,那麼可以在特定內存訪問之前或之後使用緩存刷新來實現程序的正確行爲。
如果你的硬件提供了一致的和非一致的內存類型,那麼你可以期望非連貫的內存會更快,但是如果你嘗試使用這個內存來運行並行算法,它們會以非常奇怪的方式失敗。
「一致性」意味着如果一個數據對象被多個代理(或多個路徑)訪問,則每個對象都將看到完全相同的狀態。這兩個代理可能是CPU和GPU。兩個讀取路徑的例子可以通過紋理緩存與L1緩存相比較。保持一致性通常需要額外的硬件機制,例如位用於跟蹤高速緩存行的MESI或MOESI狀態,並且可能導致大量用於傳送數據的一致性流量,特別是在代理程序數量較多的情況下。 – njuffa
GPU中的紋理緩存是「不一致」機制的典型示例。如果紋理映射下的數據發生更改,則紋理高速緩存中的任何高速緩存內容都不會失效或刷新,並且隨後訪問紋理高速緩存會導致讀取舊數據。 – njuffa