2013-09-23 25 views
1

我有一個兩階段過程,在我的模擬程序中構成一個循環。更多或更少,我有以下:對STL向量執行重複操作是否允許「固有並行性」/改進的內存訪問?

struct Coordinates 
{ 
    double * x, * y, * z; 
    uint * kind, count; 
    double GetDist(const uint p1, const uint p2); 
}; 

struct Polynomial 
{ 
    double * A, * B; 
    uint n1, n2; 
    uint Flatten(const uint i, const uint j); 
    double CalcResult(double distSq, uint kind1, uint kind2) 
    { 
     uint ij = Flatten(kind1, kind2); 
     double base = B * distSq; 
     return A[ij]*(pow(base,n2)-pow(base,n1)); 
    } 
}; 

我的問題是,如果我寫我的代碼就像

struct Model 
{ 
    Coordinates c; 
    Polynomial f; 
    double DoTest() 
    { 
     double result = 0; 
     uint count = 0; 
     std::vector<double> distSq; 
     for (uint i=0; i<c.count; i++) 
     { 
     for (uint j=i; j<c.count; j++) 
     { 
      result = c.GetDist(i,j); 
      distSq.push_back(result); 
     } 
     } 
     result = 0; 
     for (uint i=0; i<c.count; i++) 
     { 
     for (uint j=i; j<c.count; j++) 
     { 
      result += f.CalcResult(distSq[count], i, j); 
      count++; 
     } 
     } 
     return result; 
    } 
    double DoTest2() 
    { 
     double result = 0; 
     for (uint i=0; i<c.count; i++) 
     for (uint j=i; j<c.count; j++) 
      result += f.CalcResult(c.GetDist(i,j), i, j); 
     return result; 
    } 
} 

威爾Test自動啓用並行性(如矢量數學或改進了內存訪問)的x86芯片,給出它在單個數據集上的重複操作?

否則Test是一種垃圾方法 - 它使用額外的存儲空間(std::vector<double> distSq;),並且在代碼讀取方面更長。從邏輯上講它是或多或少相同,但如果我們調用GetDistf_A(功能A)和CalcResultf_B(函數B),測試是:

f_A f_A f_A ... f_A f_B f_B .... f_B 

凡爲短/更少的內存密集型功能

f_A f_B f_A f_B .... f_A f_B 

我聽說過-O#編譯的C代碼中所謂的「固有並行性」,這是由於生成的向量化數學運算等原因造成的。可能Test在x86上啓用了這種編譯器派生的並行性(例如向量化數學或優化內存訪問?)芯片,給它的對單個數據集重複操作?

(否則Test2是唯一合理的方法,因爲它使用更少的內存。)

而且將替換c樣式xyz陣列與std::vector<double>替代有加速計算或存儲器訪問的可能性以任何方式?

請不要回答「你自己的基準」...我想要通過基於編譯器的「理論基準」來測試方法Test以獲得更好的理解,固有平行度「。

回答

1

無論並行性如何,內存訪問都會導致您無法訪問。您撥打.reserve(c.count*c.count())可以防止重新分配.push_back,但這還不夠。如果c.count足夠大,則會浪費L1緩存和可能的L2。

接下來的問題是您的f_A功能取決於內存訪問。一個現代化的處理器可以同時發佈讀取和處理以前的f_B。沒有數據依賴關係。這使得Test2效率更高。

BYW,它只是我,還是CalcResult(i,j)和CalcResult(j,i)非常相似?您可能會從組合計算中受益。

我會讓ABdouble const*。畢竟,你不是通過他們寫作的。

什麼可能運作良好是#pragma omp for reduction(+, result)

+0

MSalters,它們是相同的,其實。這就是爲什麼我開始內循環@ j ---這是對唯一座標對的計算,所以我們只計算N×N座標空間的一半......關於內存訪問的好想法,謝謝......這就是我的想法...我可能會從f_A f_B開始...方法 –

+0

正確,忽略了那一點 - 在這種情況下,您只需要預留(N * N + N)/ 2個元素。仍然是O(N * N)內存訪問。 – MSalters

1

經典SIMD編譯器優化

的已知易與由編譯器SIMD指令來優化代碼一個簡單的例子如下:

for (int i = 0; i < N; ++i) 
    C[i] = A[i] + B[i]; 

SIMD optimization example with VC++

在你案例

你的第一個循環與確實看起來像所有的迭代是相互獨立的,但根據什麼GetDist實際上,結合將結果推回到一個向量中,我認爲編譯器可能比生成SIMD指令更難簡單地在內置數組中添加2個矢量的情況如何。但我不是編譯器專家,所以我可能是錯的。它也可以從編譯器到編譯器不同。

確切知道的最好方法是編譯代碼並查看反彙編以查看編譯器生成的指令類型。例如,如果您使用的是IA-32或64位Intel,請查找在MMX或XMM寄存器上執行操作的指令。您也可以嘗試用內置數組替換該矢量,以查看它是否有任何區別。

Intel assembly language reference

一個有趣的談話

我最近看了在融入本土2013會議的有趣的談話吉姆Radigan。他在Microsoft C++編譯器後端團隊工作,擅長代碼優化。他談到了幾個有趣的主題,其中包括在生成的機器代碼中實現並行性。下面是談話的鏈接:

Jim Radigan talks about compiler optimization

相關問題