如何暗示OpenMP跨步
How to hint OpenMP Stride?
我试图理解OpenMP中断循环矢量化的概念原因。此外,任何建议修复这将是有帮助的。我正在考虑手动并行化来解决这个问题,但这肯定不会很优雅,而且会导致大量的代码膨胀,因为我的代码由几个这样的部分组成,这些部分适合向量化和并行化。
我使用
OpenMPMicrosoft (R) C/c++优化编译器版本17.00.60315.1 for x64
:
没有OpenMP的info C5002:循环未矢量化,原因'502'
:
info C5001:循环矢量化
VS矢量化页面说这个错误发生在:
归纳变量的步进不是简单的+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);
}
}
}
我建议您手动进行矢量化。一个原因是自动向量化似乎不能很好地处理携带的循环依赖关系(循环展开)。
为了避免代码膨胀和神秘的内在,我使用Agner Fog的vector类。根据我的经验,它与使用intrinsic一样快,并且根据您的编译方式自动利用SSE2-AVX2 (AVX2在英特尔模拟器上进行了测试)。我使用在SSE2到AVX2上工作的矢量类编写了GEMM代码,当我在带有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]);
}
}
当你自己进行向量化时,你必须小心数组的边界。在上面的函数中,HW需要是8的倍数。有几种解决方案,但最简单和最有效的解决方案是使数组(A,B,C,D,in)稍大一点(最大7个浮点数),如果有必要,是8的倍数。然而,另一种解决方案是使用以下代码,它不要求WH是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)。vector类支持3个选项。Non-SIMD, AMD的LIBM和Intel的SVML(我在上面的代码中使用了Non-SIMD选项)。SSE和AVX的SIMD数学库
您可能需要考虑的最后一些注释。Visual Studio具有自动并行化(默认关闭)和自动矢量化(默认开启,至少在发布模式下是这样)。您可以尝试这种方法来代替OpenMP,以减少代码膨胀。http://msdn.microsoft.com/en-us/library/hh872235.aspx
另外,微软有并行模式库。由于微软对OpenMP的支持是有限的,所以值得研究一下。它几乎和OpenMP一样容易使用。这些选项中的一个可能与自动向量化一起工作得更好(尽管我对此表示怀疑)。就像我说的,我会用vectorclass手动进行向量化。
您可以尝试循环展开代替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线的合适倍数。另外,您可能会发现这个答案很有用。
- OpenMP阵列性能较差
- OpenMP卸载说'fatal error: could not find accel/nvptx-none/mkoffload'
- 使用 GCC 卸载的 OpenMP 卸载失败,并出现"Ptx assembly aborted due to errors"
- 试图对缓存进行跨步测试,但程序并没有结束
- OpenMP:并行更新数组总是需要减少数组吗
- 如何使用OpenMP并行这两个循环
- 从python调用openMP共享库时,未定义opnMP函数
- 如何使用OpenMP并行化此矩阵时间矢量运算
- 如何使用OpenMP使这个循环并行
- 如何通过替换顺序代码的while循环来添加OpenMP for循环
- 查找最近配对时的OpenMP竞赛条件
- 使用输入打破 OpenMP 中的循环
- 为什么 openmp 的并行不适用于矢量化色彩空间转换?
- 在 openmp 中,omp_get_thread_num是否绑定到物理线程?
- 在C++中使用并行化的预期速度是多少(不是 OpenMp,而是 <thread>)
- OpenMP 加上unordered_map<字符串、双字符串的缩减>
- OpenMP 与有序和关键指令并行
- 我使用 OpenMP 的线程越多,执行时间就越长,这是怎么回事?
- OpenMP for 循环并行性问题
- 如何暗示OpenMP跨步