我正在做一些图像处理,我从矢量化中受益。
我有一个矢量化ok的函数,但是我无法说服编译器输入和输出缓冲区没有重叠,因此不需要进行别名检查。
我应该可以使用__restrict__
这样做,但是如果缓冲区在作为函数参数到达时没有定义为__restrict__
,则无法让编译器相信我绝对确定2个缓冲区将会从不重叠。
这是功能:
__attribute__((optimize("tree-vectorize","tree-vectorizer-verbose=6")))
void threshold(const cv::Mat& inputRoi, cv::Mat& outputRoi, const unsigned char th) {
const int height = inputRoi.rows;
const int width = inputRoi.cols;
for (int j = 0; j < height; j++) {
const uint8_t* __restrict in = (const uint8_t* __restrict) inputRoi.ptr(j);
uint8_t* __restrict out = (uint8_t* __restrict) outputRoi.ptr(j);
for (int i = 0; i < width; i++) {
out[i] = (in[i] < valueTh) ? 255 : 0;
}
}
}
我可以说服编译器不执行别名检查的唯一方法是将内部循环放在一个单独的函数中,其中指针被定义为__restrict__
个参数。如果我将此内部函数声明为内联函数,则会再次激活别名检查。
您也可以通过此示例查看效果,我认为这是一致的:http://goo.gl/7HK5p7
(注意:我知道可能有更好的方法来编写相同的函数,但在这种情况下,我只是想了解如何避免别名检查)
修改
问题解决了!! (见answer below)
使用gcc 4.9.2,here is the complete example。请注意使用编译器标志-fopt-info-vec-optimized
代替被取代的-ftree-vectorizer-verbose=N
所以,对于gcc,请使用#pragma GCC ivdep
并享受! :)
答案 0 :(得分:4)
如果您使用的是英特尔编译器,则可以尝试包含以下行:
#pragma ivdep
以下段落引自英特尔编译器用户手册:
ivdep pragma指示编译器忽略假定的向量 依赖。为了确保代码正确,编译器会处理假定的代码 依赖是一种被证明的依赖,它阻止了矢量化。这个 pragma推翻了这个决定。只有在您知道的情况下才使用此编译指示 假定的循环依赖是可以安全忽略的。
在 gcc 中,应添加以下行:
#pragma GCC ivdep
在函数内部,在你想要向量化的循环之前(参见documentation)。这仅从gcc 4.9开始支持,顺便说一下,__restrict__
的使用是多余的。
答案 1 :(得分:2)
Another approach for this specific issue that is standardised and fully portable across (reasonably modern) compiler is to use the OpenMP simd
directive, which is part of the standard since version 4.0. The code then becomes:
void threshold(const unsigned char* inputRoi, const unsigned char valueTh,
unsigned char* outputRoi, const int width,
const int stride, const int height) {
#pragma omp simd
for (int i = 0; i < width; i++) {
outputRoi[i] = (inputRoi[i] < valueTh) ? 255 : 0;
}
}
And when compiled with OpenMP support enabled (with either full support or only partial one for simd
only, like with -qopenmp-simd
for the Intel compiler), then the code is fully vectorised.
In addition, this gives you the opportunity to indicate possible alignment of vectors, which can come handy in some circumstances. For example, had your input and output arrays been allocated with an alignment-aware memory allocator, such a posix_memalign()
with an alignment requirement of 256b, then the code could become:
void threshold(const unsigned char* inputRoi, const unsigned char valueTh,
unsigned char* outputRoi, const int width,
const int stride, const int height) {
#pragma omp simd aligned(inputRoi, outputRoi : 32)
for (int i = 0; i < width; i++) {
outputRoi[i] = (inputRoi[i] < valueTh) ? 255 : 0;
}
}
This should then permit to generate an even faster binary. And this feature isn't readily available using the ivdep
directives. All the more reasons to use the OpenMP simd
directive.
答案 2 :(得分:1)
英特尔编译器至少从版本14开始,不会在您链接的代码中为threshold2
生成别名检查,表明您的方法应该有效。然而,gcc自动矢量化器错过了这个优化机会,但确实生成了矢量化代码,正确对齐测试,混叠测试和非矢量化后备/清理代码。