为什么" #pragma omp simd"只会在" -O2"在gcc下编译?

时间:2017-12-27 09:50:53

标签: performance gcc openmp simd auto-vectorization

检查以下代码:

#include <stdio.h>
#include <omp.h>

#define ARRAY_SIZE  (1024)
float A[ARRAY_SIZE];
float B[ARRAY_SIZE];
float C[ARRAY_SIZE];

int main(void)
{   
    for (int i = 0; i < ARRAY_SIZE; i++)
    {
        A[i] = i * 2.3;
        B[i] = i + 4.6;
    }

    double start = omp_get_wtime();
    for (int loop = 0; loop < 1000000; loop++)
    {
        #pragma omp simd
        for (int i = 0; i < ARRAY_SIZE; i++)
        {
            C[i] = A[i] * B[i];
        }
    }
    double end = omp_get_wtime();
    printf("Work consumed %f seconds\n", end - start);
    return 0;
}

在我的机器上构建并运行它,它输出:

$ gcc -fopenmp parallel.c
$ ./a.out
Work consumed 2.084107 seconds

如果我评论&#34; #pragma omp simd&#34;,请再次构建并运行它:

$ gcc -fopenmp parallel.c
$ ./a.out
Work consumed 2.112724 seconds

我们可以看到&#34; #pragma omp simd&#34;并没有获得巨大的性能提升。但是,如果我添加-O2选项,则不会&#34; #pragma omp simd&#34;:

$ gcc -O2 -fopenmp parallel.c
$ ./a.out
Work consumed 0.446662 seconds

使用&#34; #pragma omp simd&#34;:

$ gcc -O2 -fopenmp parallel.c
$ ./a.out
Work consumed 0.126799 seconds

我们可以看到一个很大的进步。但是如果使用-O3,则不使用&#34; #pragma omp simd&#34;:

$ gcc -O3 -fopenmp parallel.c
$ ./a.out
Work consumed 0.127563 seconds

使用&#34; #pragma omp simd&#34;:

$ gcc -O3 -fopenmp parallel.c
$ ./a.out
Work consumed 0.126727 seconds

我们可以看到结果再次相似。

为什么&#34; #pragma omp simd&#34;只在-O2编译器下的gcc中获得了很大的性能提升?

1 个答案:

答案 0 :(得分:7)

忘记时间-O0it's a total waste of time

gcc -O3尝试自动向量化所有循环,因此使用OpenMP pragma只会帮助您进行循环,否则只会使用-ffast-mathrestrict限定符或其他障碍自动向量化在编译器必须满足纯C自动矢量化的所有可能情况下的正确性。(这里显然没有障碍:这里它不是减少,你有纯粹的垂直操作。而且你正在操作静态数组,因此编译器可以看到它们不重叠)

gcc -O2未启用-ftree-vectorize,因此如果您使用OpenMP pragma在特定循环中请求它,则只会获得自动向量化。

请注意clang-O2启用自动向量化。

GCC自动矢量化策略在OpenMP和vanilla之间可能有所不同。 IIRC,对于OpenMP循环,gcc可能只使用未对齐的加载/存储而不是标量直到到达对齐边界。如果数据在运行时对齐,即使在编译时不知道这个事实,这也没有AVX的漏洞。与gcc的大量完全展开的启动/清理代码相比,它节省了大量的代码膨胀。

有意义的是,如果您要求使用OpenMP进行SIMD矢量化,则可能需要对齐数据以避免缓存行拆分。但是C传递给float的指针比float的宽度更多的对齐这一事实并不方便。 (特别是它通常具有该属性,即使你需要该函数在极少数情况下仍然可以工作。