2013-05-13 24 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