for循环的MMX SSE扩展名

问题描述 投票:0回答:1

我有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为什么

gcc assembly compiler-optimization sse auto-vectorization
1个回答
0
投票

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将范围移位回到有符号的循环外。(即pmaxubpxor )。因此,在这种情况下,这是一个很大的错过优化,它可以将关键路径延迟降低到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缓存大小或其他内容。)

© www.soinside.com 2019 - 2024. All rights reserved.