我有GCC 9.2编译器。如果我使用MMX或SSE / AVX扩展名,则将使您的代码并行运行,因此速度会更快。如何告诉编译器使用此指令我有一个要并行的代码段:
char max(char * a, int n){
char max = (*a);
for (int i = 0 ; i< n ; ++i){
if (max < a[i]){
max = a[i];
}
}
return max;
}
它使用SSE扩展名生成代码,但不使用pmaxub为什么
SSE2是x86-64的基线,因此可以使用pmaxub
。
但是您的代码使用char
,在x86-64 System V ABI和Windows x64中使用char
= signed char
。也许您来自char
= unsigned char
的ARM公司? ISO C标准保留了char实现的签名定义,因此依靠它来确保正确性(在这种情况下为性能)是一个可怕的想法。
[如果您像普通人一样使用uint8_t
,即使没有使用-O3
或启用AVX2的任何功能,也可以从x86-64的GCC9.2 -march=skylake
获得预期的内部循环。 (Godbolt)
.L14:
movdqu xmm2, XMMWORD PTR [rax]
add rax, 16
pmaxub xmm0, xmm2
cmp rax, rdx
jne .L14
pmaxsb
requires SSE4.1。 (SSE2与MMX一样是高度非正交的,某些操作仅适用于大小和符号的某些组合,针对特定的应用程序(例如音频DSP和图形像素)。SSE4.1填补了许多空白。)
如果启用它,GCC和clang使用它。
仅使用pmaxsb
和默认的基准x86-64 -O3
(和-march
),GCC会使用-mtune=generic
(这是有符号的比较)自动矢量化,然后使用pcmpgtb
/ pand
手动混合/ pandn
和必需的额外por
复制。 movdqa
提示您编写的代码需要带符号比较,而不是无符号。叮当做同样的事情。
pcmpgtb
[GCC可以通过将范围移位输入自动向量化为.L5:
movdqu xmm1, XMMWORD PTR [rax]
add rax, 16
movdqa xmm2, xmm1
pcmpgtb xmm2, xmm0
pand xmm1, xmm2
pandn xmm2, xmm0
movdqa xmm0, xmm2
por xmm0, xmm1
cmp rax, rdx
jne .L5
的无符号,然后通过加/减128将范围移位回到有符号的循环外。(即pmaxub
和pxor
)。因此,在这种情况下,这是一个很大的错过优化,它可以将关键路径延迟降低到1个周期,仅为_mm_set1_epi8(0x80)
。
但是当然,如果您实际上启用了SSE4.1,则会得到pmaxub
。或AVX2 pmaxsb
。
您可以使用vpmaxsb
或-msse4.1
,但是通常您希望启用其他CPU也具有的扩展名,并设置调整设置。特别是对于AVX2,您不希望针对Sandybridge和较旧的CPU进行调整,因为SnB甚至没有have AVX2。您不希望拆分未对齐的载荷和类似的东西。同样,AVX2 CPU通常也具有BMI2,popcnt和其他功能。
使用-mavx2
或-march=haswell
(Zen)。或供本地使用,[-march=znver1
为您的CPU优化]。 (如果您拥有Skylake,这与使用-march=native
相同,除非它可以检测到您特定的L3缓存大小或其他内容。)