Short:pragma omp for simd
OpenMP指令是否生成使用SIMD寄存器的代码?
:如OpenMP文档中所述,“工作共享循环SIMD结构指定一个或多个相关循环的迭代将分布在已经存在的线程之间,使用SIMD指令”。从这个语句中,我期望下面的代码(simd.c)在编译运行的XMM
、YMM
或ZMM
寄存器时使用gcc simd.c -o simd -fopenmp
,但它没有使用。
#include <stdio.h>
#define N 100
int main() {
int x[N];
int y[N];
int z[N];
int i;
int sum;
for(i=0; i < N; i++) {
x[i] = i;
y[i] = i;
}
#pragma omp parallel
{
#pragma omp for simd
for(i=0; i < N; i++) {
z[i] = x[i] + y[i];
}
#pragma omp for simd reduction(+:sum)
for(i=0; i < N; i++) {
sum += x[i];
}
}
printf("%d %d\n",z[N/2], sum);
return 0;
}
在检查运行gcc simd.c -S -fopenmp
的汇编程序时,不使用SIMD寄存器。
我可以在没有OpenMP的情况下使用SIMD寄存器,使用选项-O3
,因为根据GCC文献,它包括-ftree-vectorize
标志。
XMM
寄存器:gcc simd.c -o simd -O3
YMM
寄存器:gcc simd.c -o simd -O3 -march=skylake-avx512
ZMM
寄存器:gcc simd.c -o simd -O3 -march=skylake-avx512 -mprefer-vector-width=512
但是,使用标志-march=skylake-avx512 -mprefer-vector-width=512
与-fopenmp
相结合不会生成SIMD指令。
因此,我可以在没有-O3
的情况下轻松地用pragma omp for simd
将代码向量化,而不是相反。
此时,我的目的不是生成SIMD指令,而是了解OpenMP SIMD指令在GCC中是如何工作的,以及如何仅用OpenMP (没有-O3
)来生成SIMD指令。
发布于 2020-04-11 15:45:12
至少启用-O2
以使-fopenmp
工作,一般性能也是如此
gcc simd.c -S -fopenmp
GCC的默认设置是为一致调试而优化的-O0
**,**。它永远不会使用-O0
自动矢量化,因为当C源的每个i
值都必须存在于内存中时,这是毫无意义的,等等。为什么clang使用-O0 (对于这个简单的浮点和)产生低效的asm?
同样不可能的是,您必须一次只能执行一次源行,甚至可以使用调试器在运行时修改i
或内存内容,并让程序像预期的C抽象机器那样继续运行。
在没有的情况下,任何优化都是性能的垃圾;甚至考虑到性能是否足以使用OpenMP. (当然,实际调试除外),这都是疯狂的。通常,从反优化到优化标量的加速比矢量化标量代码所能得到的要多,但两者都可能是很大的因素,所以您肯定希望优化超出自动向量化。
我可以在不使用OpenMP的情况下使用SIMD寄存器,使用选项
-O3
,因为根据GCC的文档,它包括-ftree-vectorize
标志。
好吧,那就这么做吧。-O3 -march=native -flto
通常是在编译主机上运行的代码的最佳选择。而且,-fno-trapping-math -fno-math-errno
对于所有的东西都应该是安全的,并且启用一些更好的FP函数内联,即使您不想要-ffast-math
。另外,最好是-fprofile-generate
/ -fprofile-use
配置文件引导优化(PGO),展开热循环,适当地选择分支和无分支,等等。
#pragma omp parallel
在-O3 -fopenmp
上仍然有效- GCC在默认情况下不允许自动等位基因化.
此外,#pragma omp simd
有时会使用不同的矢量化风格。在您的例子中,它似乎让GCC忘记了它知道数组是16字节对齐的,并且使用movdqu
加载(当AVX不能用于paddd xmm0, [rax]
的未对齐内存源操作数时)。比较https://godbolt.org/z/8q8Dqm -- main
调用的main._omp_fn.0:
助手函数不假定对齐。(如果GCC不费心去做向量大小的块,那么在按线程数除以数组的范围后,它可能就不能这样做了?)
使用-O2 -fopenmp
获取您所期望的
如果没有在指针args上使用OpenMP函数,让gcc更容易或更有效地将循环矢量化,让它知道数组不重叠,或者让浮点让它假装FP数学是关联的,即使您不使用-ffast-math
。
或者,如果您启用了一些优化,但不是完全优化#pragma omp
(例如,不包括-ftree-vectorize
的-O2
),那么将按您预期的方式工作。
请注意,x[i] = y[i] = i;
init循环在-O2
没有自动向量化,但是#pragma
循环是。没有-fopenmp
,纯标量。戈德波特编译器浏览器
对于这个小的-O3
,串行N
代码将运行得更快,因为线程启动开销根本不值得。但是对于大N来说,如果单个核不能饱和内存带宽(例如,在Xeon上,但是大多数双/四核桌面CPU几乎可以用一个核来满足最小带宽),并行化可能会有所帮助。或者,如果您的数组在不同核心的缓存中处于热状态。
不幸的是(?)即使GCC -O3也无法在整个代码中进行常量传播,只需打印结果。或者将z[i] = x[i]+y[i]
循环与sum(x[])
循环融合。
https://stackoverflow.com/questions/61154047
复制相似问题