【发布时间】:2020-07-24 00:20:48
【问题描述】:
简短:pragma omp for simd OpenMP 指令是否生成使用 SIMD 寄存器的代码?
更长:
正如OpenMP documentation 中所述,“worksharing-loop SIMD 构造指定一个或多个相关循环的迭代将分布在已经存在的线程中 [..] 使用 SIMD 指令”。从这个声明中,我希望以下代码 (simd.c) 在编译运行 gcc simd.c -o simd -fopenmp 时使用 XMM、YMM 或 ZMM 寄存器,但事实并非如此。
#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 寄存器。
我可以使用 -O3 选项在没有 OpenMP 的情况下使用 SIMD 寄存器,因为根据 GCC documentation
它包括-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 指令。
【问题讨论】:
-
添加 simd 子句不会改变流行编译器的成本算法。在像您这样的简单缩减循环中,循环长度 100 几乎不足以从 simd (甚至 avx512)中受益,并且可能不足以从任一循环中的 omp parallel 中受益。用于 simd 的 omp 需要生成类似于嵌套循环的东西。除非编译器可以专门针对特定的循环计数,即 simd 长度乘以线程数的倍数,否则内部和外部循环都需要运行时余数和可能的对齐代码。
-
在 gnu 编译器中使用 simd 子句的一般效果只是推翻对可能导致 simd 矢量化无效的可能别名的检测。
-
@tim18: 100 个元素足以让 128 位 SIMD 在现代 x86 上物有所值,尤其是在大小(向量宽度的倍数)和对齐方式(16 倍)已知良好的情况下。最后对向量进行水平求和的时间非常短。与某些微架构(例如某些 ARM)中向量单元只是松散耦合并且获得标量向量结果存在很大延迟不同,x86 对于
movd eax, xmm0只有几个周期延迟。
标签: c gcc openmp vectorization simd