试图理解常量大小/对齐指针上的clang/gcc\u内置内存集

试图理解常量大小/对齐指针上的clang/gcc\u内置内存集,gcc,clang,compiler-optimization,micro-optimization,memset,Gcc,Clang,Compiler Optimization,Micro Optimization,Memset,基本上,我试图理解为什么gcc/clang都使用xmm注册它们的\uuuu内置内存集,即使内存目标和大小都可以被ymm的大小整除(或zmm),CPU也支持AVX2AVX512 为什么GCC在没有任何SIMD的中等大小的值上实现\uuu内置\u memset(同样假设CPU支持SIMD) 例如: __builtin_memset(__builtin_assume_aligned(ptr, 64), -1, 64)); 将编译为: vpcmpeqd %xmm0, %x

基本上,我试图理解为什么gcc/clang都使用
xmm
注册它们的
\uuuu内置内存集
,即使内存目标和大小都可以被
ymm
的大小整除(或
zmm
),CPU也支持
AVX2
AVX512

为什么GCC在没有任何SIMD的中等大小的值上实现
\uuu内置\u memset
(同样假设CPU支持SIMD)

例如:

__builtin_memset(__builtin_assume_aligned(ptr, 64), -1, 64));
将编译为:

        vpcmpeqd        %xmm0, %xmm0, %xmm0
        vmovdqa %xmm0, (%rdi)
        vmovdqa %xmm0, 16(%rdi)
        vmovdqa %xmm0, 32(%rdi)
        vmovdqa %xmm0, 48(%rdi)
我试图理解为什么选择这个而不是像这样的东西

        vpcmpeqd        %ymm0, %ymm0, %ymm0
        vmovdqa %ymm0, (%rdi)
        vmovdqa %ymm0, 32(%rdi)
如果将
\uuuu内置\u memset
AVX2
指令混合使用,它们仍然使用
xmm
,因此肯定不会保存
vzeropper

第二,对于GCC的
\uuuuuuuu内置\uu memset(\uuuuuu内置\uu假定\uu对齐(ptr,64),-1512)
GCC将其实现为:

        movq    $-1, %rdx
        xorl    %eax, %eax
.L8:
        movl    %eax, %ecx
        addl    $32, %eax
        movq    %rdx, (%rdi,%rcx)
        movq    %rdx, 8(%rdi,%rcx)
        movq    %rdx, 16(%rdi,%rcx)
        movq    %rdx, 24(%rdi,%rcx)
        cmpl    $512, %eax
        jb      .L8
        ret
为什么gcc会选择使用
xmm
(或
ymm
/
zmm
)寄存器的循环

是一个带有示例(和其他一些示例)的锁紧螺栓链接

多谢各位


编辑:clang使用ymm(但不是zmm)

其中一些可能与此相关。谢谢!这似乎主要是关于AVX512与AVX2的优缺点,而GCC仍在使用SSE。看起来GCC不知道如何在循环中使用
vmovdqa
来内联扩展memset。对于最多256个字节,它将使用
vmovdqa xmm
完全展开,但对于更多字节,它将使用4x
movq%r64,m64
在循环中使用愚蠢的索引寻址模式。(或者对于
-march=sandybridge
中需要取消层压的地方,使用
rep stosq
)。@Noah:
rep stosq
有很多启动开销。标量循环即使在Core2这样的古老CPU上也能处理不对齐问题,在Core2中,
movdqu
非常昂贵,对于小计数来说也不错。但是,对于对齐的数据,在一个循环中,它永远不会超过一半的
movdqa
,即使它在奔腾M上解码到多个UOP(两个64位的一半),也可能不会。GCC的memset扩展调优可能已经有一段时间没有被关注了,也许从那以后就没有了?而且不利用对齐,非常原始。@Noah:
movdqa-xmm
从来没有这个问题。使用两倍数量的标量存储总是愚蠢的。避免使用YMM有时是有意义的(如果周围的代码也不使用它们),但是
movq%rdx,(%rdi,%rcx)
。。。循环从来没有任何意义。(除非在没有已知对齐的情况下为Core2或更早版本进行调整,这里两者都不正确。)其中一些可能是相关的。谢谢!这似乎主要是关于AVX512与AVX2的优缺点,而GCC仍在使用SSE。看起来GCC不知道如何在循环中使用
vmovdqa
来内联扩展memset。对于最多256个字节,它将使用
vmovdqa xmm
完全展开,但对于更多字节,它将使用4x
movq%r64,m64
在循环中使用愚蠢的索引寻址模式。(或者对于
-march=sandybridge
中需要取消层压的地方,使用
rep stosq
)。@Noah:
rep stosq
有很多启动开销。标量循环即使在Core2这样的古老CPU上也能处理不对齐问题,在Core2中,
movdqu
非常昂贵,对于小计数来说也不错。但是,对于对齐的数据,在一个循环中,它永远不会超过一半的
movdqa
,即使它在奔腾M上解码到多个UOP(两个64位的一半),也可能不会。GCC的memset扩展调优可能已经有一段时间没有被关注了,也许从那以后就没有了?而且不利用对齐,非常原始。@Noah:
movdqa-xmm
从来没有这个问题。使用两倍数量的标量存储总是愚蠢的。避免使用YMM有时是有意义的(如果周围的代码也不使用它们),但是
movq%rdx,(%rdi,%rcx)
。。。循环从来没有任何意义。(除非在没有已知对齐的情况下为Core2或更早版本进行调谐,此处两者均不正确。)