使用 openMP simd 进行以下循环是否安全?

Is it safe to use openMP simd for following loops?

本文关键字:循环 是否 安全 openMP simd 使用      更新时间:2023-10-16

我只是更仔细地研究OpenMP simd结构,并且有三个循环似乎没有被gcc(简短的性能测试)矢量化,但我认为它们可以。所以我想知道,添加 simd 编译指示是否安全,以及为什么 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 编译指示,代码可以更快。由于内部循环,自动矢量化是不可能的吗?

对于第二个示例,我尝试了 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编译指示的性能应该更好吗?一些简短的测试表明情况确实如此,但是如果由于 std::sqrt 而无法进行自动矢量化,为什么编译指示可以呢?

谢谢你的帮助! :)

对于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 矢量化sqrtf(使用牛顿方法的一次迭代)。GCC和ICC分别用libmvec(_ZGVbN4v_sinf)和SVML(__svml_sinf4)对sinf进行矢量化。Clang不矢量化sinf。见神霹雳。sqrt是一种特殊情况(因为 x86 指令集具有矢量化sqrt指令),可以在没有矢量化数学库的情况下内联。