2013-05-13 79 views
3

我想了解爲什麼OpenMP打破循環矢量化的概念原因。此外,任何修復此問題的建議都會有所幫助。我正在考慮手動並行化這個問題來解決這個問題,但那肯定不會很優雅,並導致大量的代碼膨脹,因爲我的代碼由幾個這樣的部分組成,這些部分可以用於向量化和並行化。如何提示OpenMP步伐?

我使用

微軟(R)C/C++優化編譯器版本17.00.60315.1針對x64

對於OpenMP:

信息C5002:循環不矢量化由於原因'502'

沒有OpenMP的:

信息C5001:環矢量

的VS vectorization page說,這個錯誤發生在:

感應變量以某種方式加強不是一個簡單的其他+1

我可以強制它跨步1嗎?

循環

#pragma omp parallel for 
for (int j = 0; j < H*W; j++)//A,B,C,D,IN are __restricted 
{ 
    float Gs = D[j]-B[j]; 
    float Gc = A[j]-C[j]; 
    in[j]=atan2f(Gs,Gc); 
} 

盡力而爲

#pragma omp parallel 
{// This seems to vectorize, but it still requires quite a lot of boiler code 
    int middle = H*W/2; 
    #pragma omp sections nowait 
    { 
     #pragma omp section 
     for (int j = 0; j < middle; j++) 
     { 
      float Gs = D[j]-B[j]; 
      float Gc = A[j]-C[j]; 
      in[j]=atan2f(Gs,Gc); 
     } 
     #pragma omp section 
     for (int j = middle; j < H*W; j++) 
     { 
      float Gs = D[j]-B[j]; 
      float Gc = A[j]-C[j]; 
      in[j]=atan2f(Gs,Gc); 
     } 
    } 
} 
+0

令人驚訝的是,由於atan2f函數,Visual Studio對此進行了矢量化。我還沒有嘗試用Visual Studio編譯它,但是使用GCC它沒有矢量化(有或沒有OpenMP)。在我的經驗中,GCC自動矢量化比Visual Studio更好。你最近問的賞金問題如果你使用了GCC,那麼它就沒有那麼有趣了,因爲GCC的帽子沒有任何問題向量化短褲的循環。但是,也許這是Visual Studio的自動矢量化更好的一個例子。 – 2013-05-13 17:49:01

+0

我在Visual Studio中試過這個。它像你說的那樣矢量化。我真的很驚訝。我沒有測試過性能。我想知道Visual Studio爲atan2f函數做了什麼。它真的有SSE/AVX atan2f功能嗎? – 2013-05-14 12:48:09

回答

2

我建議您手動進行矢量化。一個原因是自動矢量化似乎不能很好地處理循環依賴(循環展開)。

爲了避免代碼膨脹和神祕的內在因素,我使用了Agner Fog的vectorclass。根據我的經驗,它與使用內在函數一樣快,它會根據您的編譯方式自動利用SSE2-AVX2(在英特爾仿真器上測試AVX2)。我已經寫了使用Vector類的GEMM代碼,它可以在SSE2上運行到AVX2,當我在AVX系統上運行時,我的代碼已經比僅使用SSE的Eigen快。這裏是你的vector類的函數(我沒有嘗試展開循環)。

#include "omp.h" 
#include "math.h" 

#include "vectorclass.h" 
#include "vectormath.h" 

void loop(const int H, const int W, const int outer_stride, float *A, float *B, float *C, float *D, float* in) { 
    #pragma omp parallel for 
    for (int j = 0; j < H*W; j+=8)//A,B,C,D,IN are __restricted, W*H must be a multiple of 8 
    { 
     Vec8f Gs = Vec8f().load(&D[j]) - Vec8f().load(&B[j]); 
     Vec8f Gc = Vec8f().load(&A[j]) - Vec8f().load(&C[j]); 
     Vec8f invec = atan(Gs, Gc); 
     invec.store(&in[j]); 
    } 

} 

當你自己做矢量化時,你必須小心數組邊界。在上面的函數H 中,W需要是8的倍數。有幾種解決方案,但最簡單和最有效的解決方案是使陣列(A,B,C,D,in)稍大一點(最大7個浮點數),如果有必要爲8的倍數。然而,另一個解決方案是使用下面的代碼,它不需要W H是8的倍數,但它不那麼漂亮。

#define ROUND_DOWN(x, s) ((x) & ~((s)-1)) 
void loop_fix(const int H, const int W, const int outer_stride, float *A, float *B, float *C, float *D, float* in) { 
    #pragma omp parallel for 
    for (int j = 0; j < ROUND_DOWN(H*W,8); j+=8)//A,B,C,D,IN are __restricted 
    { 
     Vec8f Gs = Vec8f().load(&D[j]) - Vec8f().load(&B[j]); 
     Vec8f Gc = Vec8f().load(&A[j]) - Vec8f().load(&C[j]); 
     Vec8f invec = atan(Gs, Gc); 
     invec.store(&in[j]); 
    } 
    for(int j=ROUND_DOWN(H*W,8); j<H*W; j++) { 
     float Gs = D[j]-B[j]; 
     float Gc = A[j]-C[j]; 
     in[j]=atan2f(Gs,Gc); 
    } 

} 

自己做矢量化的一個挑戰是找到一個SIMD數學庫(例如atan2f)。 vectorclass支持3個選項。非SIMD,AMD的LIBM和Intel的SVML(我在上面的代碼中使用了非SIMD選項)。 SIMD math libraries for SSE and AVX

您可能要考慮的最後一些評論。 Visual Studio具有自動並行化(默認關閉)以及自動矢量化(默認情況下,至少在發佈模式下)。你可以試試這個,而不是OpenMP來減少代碼膨脹。 http://msdn.microsoft.com/en-us/library/hh872235.aspx

另外,Microsoft有並行模式庫。由於微軟的OpenMP支持有限,因此值得關注。這幾乎與OpenMP一樣易於使用。有可能這些選項中的一個更適合自動矢量化(儘管我懷疑它)。就像我說的那樣,我會使用vectorclass手動進行矢量化。

1

您可以嘗試循環,而不是展開的sections(?):

#pragma omp parallel for 
for (int j = 0; j < H*W; j += outer_stride)//A,B,C,D,IN are __restricted 
{ 
    for (int ii = 0; ii < outer_stride; ii++) { 
    float Gs = D[j+ii]-B[j+ii]; 
    float Gc = A[j+ii]-C[j+ii]; 
    in[j+ii] = atan2f(Gs,Gc); 
    } 
} 

其中outer_stride是您的SIMD產品線的合適倍數。另外,你可能會發現這個answer有用。

+0

我喜歡你的建議。我在GCC中試過,但由於atan2f函數,它根本沒有向量化。我很驚訝GCC在Visual Studio中沒有進行矢量化。我編譯了「g ++ foo.cpp -o foo -O3 -fopenmp -ftree -vectorizer -verbose = 2 -fast-math」 – 2013-05-13 17:45:47

+0

我認爲循環展開會導致與原始循環相同的麻煩。具體而言,VS並不確定這一步,從表現來看似乎這些循環沒有被矢量化。 – Mikhail 2013-05-13 22:36:03

+0

我在VS中嘗試了你的建議,它沒有矢量化。其實,我很驚訝原始循環矢量化。如果Visual Studio有atan2f的SSE/AVX版本,我會感到非常驚訝。 – 2013-05-14 13:03:35