3

检查以下代码:

#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

如果我注释掉“ #pragma omp simd”,再次构建并运行它:

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

我们可以看到 " #pragma omp simd" 并没有获得很大的性能提升。但是如果我添加-O2选项,则没有“ #pragma omp simd”:

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

带“ #pragma omp simd”:

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

我们可以看到很大的进步。但如果使用-O3,则没有“ #pragma omp simd”:

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

与“ #pragma omp simd”:

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

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

为什么“ ”只在编译器下#pragma omp simd有很大的性能提升?-O2gcc

4

1 回答 1

10

忘记时间-O0这完全是浪费时间

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

gcc -O2不启用-ftree-vectorize,因此只有在使用 OpenMP 编译指示在特定循环上请求它时才能获得自动矢量化。


请注意,clang在 处启用自动矢量化-O2


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

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

于 2017-12-27T10:12:04.703 回答