我只是更仔细地研究OpenMP simd构造,并且有三个循环看起来似乎没有被gcc(简短的性能测试)矢量化,但我认为他们可以。所以我想知道,添加simd pragma是否安全以及为什么gcc没有对它们进行矢量化。
首先是矩阵乘法,其值存储为单个数组:
#pragma omp parallel for
for(size_t row = 0; row < 100; ++row){
{#pragma omp simd}
for(size_t col = 0; col < 100; ++col){
float sum = c[row * 100 + col];
for(size_t k = 0; k < 100; k++){
sum += a[rows * 100 + k] * b[k * 100 + col];
}
c[row * 100 + col] = sum;
}
我知道b没有转置,这会影响性能。通过添加simd pragma,代码变得更快。由于内循环,自动矢量化是不可能的吗?
对于第二个例子,我正在尝试OpenMP的自定义缩减声明功能,这实际上并不需要。
#pragma omp declare reduction(sum : double : omp_out += omp_in) initializer(omp_priv = omp_orig)
double red_result = 0;
#pragma omp parallel for {simd} reduction(sum:red_result)
for(size_t i = 0; i < 100; ++i){
red_result = red_result + a[i];
}
减少会阻止矢量化吗?因为我认为它应该可以正常工作?
最后一个例子是一个复杂的循环,带有另一个内部循环和函数调用。简化它看起来像这样:
#pragma omp parallel for {simd}
for(size_t i = 0; i < 100; ++i){
[..]
for(size_t j = 0; j < 100; j++){
if(j != i){
float k2 = a[i] - b[j];
k = std::sqrt(k2);
}
}
[do more with k]
}
所以这里的问题可能是sqrt调用,无法进行矢量化?但是,如果使用simd pragma,性能会更好吗?一些简短的测试表明情况就是这样,但是如果由于std :: sqrt而无法实现自动矢量化,为什么pragma可以实现?
感谢您的帮助! :)
答案 0 :(得分:3)
对于math.h
中的数学函数,编译器需要实现数学函数的矢量化版本。 GCC使用libmvec执行此操作,ICC使用SVML执行此操作。据我所知,Clang对矢量化数学函数没有原生支持。
让我们考虑以下代码:
void foo(float * __restrict a, float * __restrict b) {
a = (float*)__builtin_assume_aligned(a, 16);
b = (float*)__builtin_assume_aligned(b, 16);
for(int i = 0; i < 100; ++i) {
b[i] = sqrtf(a[i]);
}
}
void foo2(float * __restrict a, float * __restrict b) {
a = (float*)__builtin_assume_aligned(a, 16);
b = (float*)__builtin_assume_aligned(b, 16);
for(int i = 0; i < 100; ++i) {
b[i] = sinf(a[i]);
}
}
GCC,ICC和Clang vectorize sqrtf
(使用牛顿方法的一次迭代)。 GCC和ICC分别使用libmvec(sinf
)和SVML(_ZGVbN4v_sinf
)向量__svml_sinf4
。 Clang没有矢量化sinf
。见godbolt。 sqrt
是一种特殊情况(因为x86指令集具有向量化的sqrt
指令),可以在没有向量化数学库的情况下进行内联。