在SSE寄存器中存储常量(GCC,C ++)

时间:2015-02-23 17:09:12

标签: c++ c assembly sse inline-assembly

Hello StackOverflow社区

我遇到了以下挑战:在我的C ++应用程序中,我有一个非常复杂的(立方体)循环,其中,在所有深度,我执行以下操作:

  1. 计算4个浮点值
  2. 将所有4个值乘以常数
  3. 将浮动转换为整数
  4. 这个代码将在每个循环中运行数千次迭代(导致数十亿次操作),我想尽可能快地完成它,所以我试图利用SSE处理器指令。

    在尝试手动优化代码时,我遇到了以下障碍:每次使用常量乘以所有值时,必须将常量加载到XMM寄存器。我的想法是保留一个寄存器(并禁止编译器使用它),加载一次值,然后用一个特定的寄存器对乘法进行硬编码,但是我找不到正确的方法。

    顺便问一下,有人可以向我解释,为什么这段代码:

        vmovaps .LC0(%rip), %xmm1
        movl    $1000000000, %eax
        vmovaps .LC1(%rip), %xmm0
        .p2align 4,,10
        .p2align 3
    .L2:
    #APP
    # 26 "sse.cpp" 1
        .intel_syntax noprefix;
        mulps %xmm1,%xmm0;
        .att_syntax prefix;
    
    # 0 "" 2
    #NO_APP
        subl    $1, %eax
        jne     .L2
    

    比下一个更糟糕(实际0m1.656s vs真实0m1.618s):

        vmovaps .LC0(%rip), %xmm1
        movl    $1000000000, %eax
        vmovaps .LC1(%rip), %xmm0
        .p2align 4,,10
        .p2align 3
    .L2:
        vmulps  %xmm0, %xmm1, %xmm1
        subl    $1, %eax
        jne     .L2
    

    (不同之处在于我在gcc [in snippet]中的inline asm和兼容性的旧SSE指令中使用了intel语法,而gcc使用AVX向量自动生成版本[second snippet])

1 个答案:

答案 0 :(得分:2)

需要注意的是,您需要更具体地说明如何编译事物并提供最少的示例。我知道这可能不是最佳答案,但我认为这已经足够了。它很长,但这是因为代码。

以下工作的底线是,为编译器保留并使用适当的编译器标志应该是安全的。在底部我举例说明如何使用本地寄存器变量,但它可能赢了不是很有用(很容易被忽略)。您可以使用全局寄存器变量,但它不会产生任何好的结果,并且不鼓励。

我的设置为Intel(R) Core(TM) i7-4770 CPUgcc version 4.9.2clang version 3.5.0。下面的代码会将avx_scalar存储在xmm以及-O1及更高版本的注册表中。没有任何内容或-O0他们没有。生成程序集的代码是:

[clang++|g++] -march=native -S -Ox ./sse.cpp

其中x是优化级别。

有趣的是,-march=archive使用smmintrin.h两个编译器决定在我测试的任何情况下使用SSE4.1版本而不是传统的SSE,即使我在代码本身使用了传统的SSE内在函数。这很好。

我还使用error: "SSE4.1 instruction set not enabled"进行了测试,这是SSE4.1标头。在没有标志的情况下,gcc使用遗留SSE并且clang无法使用xmmintrin.h进行编译。 avx.cpp是传统的SSE标头,两个编译器在存在标志时生成AVX版本,在没有标志时生成遗留版本。

测试代码extern "C" { #include <smmintrin.h> } const float scalar = 3.14; const __m128 avx_scalar = _mm_set1_ps(scalar); __m128 vector; __m128 its_me(){ __m128 ret; __m128 result; for(int i = 0; i < 1000; ++i) { vector = _mm_set_ps(i*1,i*2,i*3,i*4); result = _mm_mul_ps(vector, avx_scalar); ret = _mm_add_ps(ret, result); } return ret; }

g++ -march=native -S -O2 ./avx.cpp

.LFB639: .cfi_startproc vmovaps _ZL10avx_scalar(%rip), %xmm5 xorl %edx, %edx .p2align 4,,10 .p2align 3 .L2: leal (%rdx,%rdx), %ecx vxorps %xmm2, %xmm2, %xmm2 vxorps %xmm1, %xmm1, %xmm1 vxorps %xmm3, %xmm3, %xmm3 leal 0(,%rdx,4), %eax vcvtsi2ss %ecx, %xmm3, %xmm3 vxorps %xmm4, %xmm4, %xmm4 vcvtsi2ss %eax, %xmm2, %xmm2 leal (%rcx,%rdx), %eax vcvtsi2ss %edx, %xmm4, %xmm4 addl $1, %edx vcvtsi2ss %eax, %xmm1, %xmm1 vunpcklps %xmm4, %xmm3, %xmm3 vunpcklps %xmm1, %xmm2, %xmm1 vmovlhps %xmm3, %xmm1, %xmm1 vmulps %xmm5, %xmm1, %xmm2 vaddps %xmm2, %xmm0, %xmm0 cmpl $1000, %edx jne .L2 vmovaps %xmm1, vector(%rip) ret .cfi_endproc 的隐性部分:

clang++ -march=native -S -O2 ./avx.cpp

# BB#0: xorl %eax, %eax movl $4, %ecx movl $2, %edx vmovaps _ZL10avx_scalar(%rip), %xmm1 xorl %esi, %esi # implicit-def: XMM0 .align 16, 0x90 .LBB0_1: # =>This Inner Loop Header: Depth=1 leal -2(%rdx), %r8d leal -4(%rcx), %edi vmovd %edi, %xmm2 vpinsrd $1, %eax, %xmm2, %xmm2 vpinsrd $2, %r8d, %xmm2, %xmm2 vpinsrd $3, %esi, %xmm2, %xmm2 vcvtdq2ps %xmm2, %xmm2 vmulps %xmm1, %xmm2, %xmm2 vaddps %xmm2, %xmm0, %xmm0 leal 1(%rsi), %r8d leal 3(%rax), %edi vmovd %ecx, %xmm2 vpinsrd $1, %edi, %xmm2, %xmm2 vpinsrd $2, %edx, %xmm2, %xmm2 vpinsrd $3, %r8d, %xmm2, %xmm2 vcvtdq2ps %xmm2, %xmm2 vmulps %xmm1, %xmm2, %xmm3 vaddps %xmm3, %xmm0, %xmm0 addl $2, %esi addl $6, %eax addl $8, %ecx addl $4, %edx cmpl $1000, %esi # imm = 0x3E8 jne .LBB0_1 # BB#2: vmovaps %xmm2, vector(%rip) retq

-01

只是为了记录,你可以手动将一个局部变量放入注册,但clang完全忽略,gcc忽略xmm13及以上。我鼓励寻找g++ -march=native -S -Ox ./avx.cppx的输出中,下面的代码具有不同的extern "C" { #include <xmmintrin.h> } const float scalar = 3.14; __m128 its_me(){ __m128 vector; register __m128 avx_scalar asm ("xmm13") = _mm_set1_ps(scalar); // that's how you do it in gcc. //const __m128 avx_scalar = _mm_set1_ps(scalar); __m128 ret; __m128 result; for(int i = 0; i < 1000; ++i) { vector = _mm_set_ps(i*1,i*2,i*3,i*4); result = _mm_mul_ps(vector, avx_scalar); ret = _mm_add_ps(ret, result); } return ret; } 值(假设您的cpu上至少有13个xmm寄存器):

{{1}}