假设我有这样的函数:
void test32(int* a, int* b, size_t n) {
for (size_t i = 0; i < n; ++i) {
a[i] = a[i] + b[i];
}
}
Clang和gcc在用-O3 -march=core-avx2
(哥德波特)编译时都会产生256位的SIMD .
现在假设我有这样的函数:
void test128(__m128i* a, __m128i* b, size_t n) {
for (size_t i = 0; i < n; ++i) {
a[i] = _mm_add_epi32(a[i], b[i]);
}
}
使用相同的CFLAGS,clang和gcc都拒绝将其矢量化为256位(哥德波特)。
因此,天真代码(自动向量化)每次迭代处理的元素是手动向量化SSE2代码的两倍。这有什么意义?当AVX2可用时,是否有一种方法可以指示编译器将128位SIMD的本质向量化为256位?
发布于 2022-07-22 21:08:14
不幸的是,我不知道有一个编译器选项可以将本质(或GNU C本机向量)重新向量化为更广泛的类型。,这是首先不对容易自动向量化的情况手动向量化的原因之一。。
有时,能够告诉编译器您希望它使用什么向量化策略是有用的,这就是本质的用途。
如果编译器过于积极地重写它们,在某些情况下这将是不好的。就像带有更宽向量的循环之后的清理循环一样,您可能会使用128位来减少标量元素。或者,您可能有16字节对齐,而不是32字节对齐,并且您特别关心沙桥(其中32字节加载/存储错误是非常糟糕的)。或者在Haswell服务器上,256位AVX可以减少最大turbo (至少对于FP数学指令),所以您只想在程序的某些阶段运行的函数中使用256位向量。
从根本上说,这是一种权衡,一种是用asm编写,对于聪明的人来说,指定他们想要的是什么,而只是给出一种方法,以一种它能够理解和优化的方式告诉编译器程序逻辑(就像+
操作符:这并不意味着你会得到一个add
指令)。
MSVC和ICC比GCC/clang更倾向于从字面上看待本质,而不是不断地通过它们进行传播。GCC/clang关于如何对待本质的选择在很多方面都是明智的。
如果您有这样一个微不足道的向量化问题(没有循环携带依赖项或洗牌),并且希望您的代码可以为将来更广泛的向量指令进行编译,使用OpenMP 来告诉编译器您肯定希望将其向量化。如果不启用完全优化以使编译器尝试自动向量化每个循环,那么OpenMP就是要解决的问题。(在gcc -O3
,或叫clang -O2
。( -O2 for GCC12.)OpenMP可以使编译器以通常不允许使用-ffast-math
的方式将FP数学向量化,例如FP缩减(如数组和)。
https://stackoverflow.com/questions/73088104
复制