检查以下代码:
#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 secondsn", 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
"仅在gcc
编译器下仅在-O2
中进行大幅度改进?
忘记与-O0
的计时,这是一个浪费时间。
gcc -O3
尝试自动化所有循环,因此,使用OpenMP Pragmas仅帮助您进行循环,否则否则只能使用-ffast-math
,restrict
预选赛自动进行自动矢量,或在所有可能的情况下都必须满足的情况下的正确性障碍或其他障碍纯C的自动矢量化(显然在这里没有障碍:这里不是减少的,您拥有纯粹的垂直操作。您在静态阵列上进行操作,因此编译器可以看到它们不会重叠)
gcc -O2
不启用-ftree-vectorize
,因此,只有使用OpenMP Pragmas在特定循环中要求它,才能自动矢量化。
请注意,clang
在-O2
启用自动矢量化。
gcc自动向导策略在OpenMP和香草之间可能有所不同。IIRC,对于OpenMP循环,GCC可能只使用非对齐的载荷/商店而不是标量表,直到达到对齐边界为止。如果数据在运行时对齐,即使在编译时不知道该事实,AVX就没有perf的方面。它节省了很多代码膨胀与GCC的大量全面启动/清理代码。
有意义的是,如果您要求使用OpenMP进行SIMD矢量化,则您可能会对齐数据以避免缓存线拆分。但是C传递给float
的指针比float
的宽度更一致的事实并不是很方便。(尤其是通常具有该属性,即使您需要该功能在极少数情况下仍然可以使用)。