2015-02-23 61 views
5
讀取

我的問題是,在下面的語句從書的方面:陣列隨機結構(AOS)與數組(SOA)結構的量化

不幸的是,SOA的形式,並不是在所有的理想情況。對於 隨機或不連貫的情況,收集用於訪問數據,SoA表單可能會導致額外的不需要的數據被讀取到緩存中,從而降低性能。在這種情況下,使用AoS 表單代替將導致較小的工作集和提高的性能。但是,通常情況下,如果計算結果爲向量化的 ,則首選SoA表單。

爲什麼會導致更好的性能AOS我猜測是不同的時,或更好的全部,在相同的結構域都參與了單矢量運行。

實施例(只是一個概念,沒有具體的,或者工作的代碼在所有):

/*Note that the types of data I maintain the same intentionally, 
    to simplify discussion*/ 
struct Data { 
    float mean; 
    float distribution[10] 
} 

和定義這些的陣列得到隨機從一些數據源

Data aos[5];

現在,如果在向量化循環期間我做類似的事情:

float* dataPtr = &(aos[0].mean); 

#pragma simd 
for(int i=0; i< 60; i++) 
{ 
    const float mean = (*dataPtr); 
    /*do something with mean */ 

    dataPtr++; 

    /*do something with distribution */ 
} 

這將導致更好的性能,在SoA的情況下,我會在緩存行上推送更多信息,以便在計算過程中實際需要。一些CPU預緩存?在AoS的情況下,取而代之的是更好的性能。

我的假設是正確的還是還有別的?

+3

可怕的三字母首字母縮略詞:SoA =陣列結構,AoS =結構陣列。 – 2015-02-23 14:59:31

+0

@HansPassant:他們就是這樣稱呼的,寫下完整的名字會讓標題太長而且不難看。 – Tigran 2015-02-23 15:01:51

+3

@Tigran:爲了那些不瞭解你的書的術語的人的利益,定義一次術語並不需要很長時間。 – 2015-02-23 15:05:24

回答

8

您可以通過兩種方式並行化您的程序:水平和垂直。我認爲你正在混合這兩種方法。

水平並行化將SIMD單元中的每個通道作爲獨立的「線程」處理不同的數據。垂直並行化使整個SIMD單元在相同的數據對象上工作,試圖從其內部的多維度中受益。

舉一個具體的例子:考慮你有2個數組XY你想添加的3D向量。

  • 水平的方法:所述SIMD單元的每個車道會做:

    for(idx = 0; idx<size; idx+=SIMD_size) { 
        ... = X[idx+laneid].x + Y[idx+laneid].x; 
        ... = X[idx+laneid].y + Y[idx+laneid].y; 
        ... = X[idx+laneid].z + Y[idx+laneid].z; 
    } 
    
  • 垂直的方法:所述SIMD單元的每個車道取相同向量的不同分量:

    for(idx = 0; idx<size; idx+=1) { 
        ... = X[idx].coord(laneid) + Y[idx].coord(laneid); 
    } 
    

垂直方法更容易實現。事實上,編譯器正在嘗試自動矢量化。問題是,隨着SIMD單元的寬度不斷增長,實施不能從中受益。如果您從4寬轉換爲16寬SIMD,那麼您的3D矢量仍然只能並行添加3個數字。

水平方法更難。你通常必須處理分歧的分支,函數調用等......並且 - 你想將數據重新組織到數組結構中 - 以便不同數據對象的相應字段在內存中彼此相鄰。


現在,回到你的問題:SOA使感只有當你做橫向並行。當每個通道訪問不同對象的相同字段時,SoA允許用更好的對齊的單個內存提取來代替昂貴的收集指令。 如果你試圖做垂直,就像你在問題中的例子 - 沒有人會考慮首先做SoA - 訪問同一對象的多個字段會導致「聚集」。

但是,對於隨機訪問,即使執行水平並行化,SoA也可能不是最佳選項。首先,你沒有SoA的好處,因爲你仍然需要做昂貴的聚會。但是,由於同一對象的字段分佈在內存中,因此每個負載都會碰到不同的緩存通道。不僅增加了內存帶寬的使用,還可能導致緩存抖動。 這就是爲什麼SoA在隨機訪問方面效率不高的原因。

更好的解決方案是採用混合方法:將數據打包爲SIMD-size結構的數組結構。但這是另一回事......

+1

可能要提到SoA的病態情況 - 其中同一邏輯結構的兩個組件具有緩存行爭用(從一個讀取導致另一個卸載) – Yakk 2015-02-23 15:23:10

1

是的,你似乎瞭解情況。

如果您從相同的結構中讀取多個值,那麼CPU將只需要獲取它需要的那些結構成員的緩存行數 - 如果結構成員佈局合理,可能只有一個。所以緩存可能是這樣的(其中v是你想要的值,而空插槽其他值)

line 1: | v | | v | v | | | v | | 

如果每個這些值都必須從一個單獨的數組讀取,那麼它將不得不去取整個緩存行爲每個值。所以緩存可能看起來像

line 1: | | | v | | | | | | 
line 2: | | | | | v | | | | 
line 3: | | v | | | | | | | 
line 4: | | | v | | | | | | 

如果你通過數組爲了工作,那是很好的 - 你很快就會需要的是爲獲取額外的價值。然而,如果你不按順序工作(用本書的話來說,你處於「隨機或不連貫的情況」),那麼每次獲取比你需要的更多的東西會浪費緩存中的空間,如果所需的值在一個結構中一起使用,最終會使用更多的內存帶寬。