Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/vim/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么clang使用-O0(对于这个简单的浮点和)生成低效的asm?_C_Assembly_X86 64_Compiler Optimization_Llvm Codegen - Fatal编程技术网

为什么clang使用-O0(对于这个简单的浮点和)生成低效的asm?

为什么clang使用-O0(对于这个简单的浮点和)生成低效的asm?,c,assembly,x86-64,compiler-optimization,llvm-codegen,C,Assembly,X86 64,Compiler Optimization,Llvm Codegen,我正在llvm clang Apple llvm 8.0.0版(clang-800.0.42.1)上反汇编此代码: 我使用no-O规范进行编译,但我也尝试使用-O0(给出相同的结果)和-O2(实际计算值并存储预计算的值) 由此产生的拆卸如下(我拆下了不相关的零件) ->0x10000F30:pushq%rbp 0x10000F31:movq%rsp,%rbp 0x10000F34:subq$0x10,%rsp 0x10000F38:leaq 0x6d(%rip),%rdi 0x10000F3F:

我正在llvm clang Apple llvm 8.0.0版(clang-800.0.42.1)上反汇编此代码:

我使用no-O规范进行编译,但我也尝试使用-O0(给出相同的结果)和-O2(实际计算值并存储预计算的值)

由此产生的拆卸如下(我拆下了不相关的零件)

->0x10000F30:pushq%rbp
0x10000F31:movq%rsp,%rbp
0x10000F34:subq$0x10,%rsp
0x10000F38:leaq 0x6d(%rip),%rdi
0x10000F3F:movss 0x5d(%rip),%xmm0
0x10000F47:MOVS 0x59(%rip),%xmm1
0x10000F4F:movss%xmm1,-0x4(%rbp)
0x10000F54:movss%xmm0,-0x8(%rbp)
0x10000F59:movss-0x4(%rbp),%xmm0
0x100000f5e:地址-0x8(%rbp),%xmm0
0x100000f63:movss%xmm0,-0xc(%rbp)
...
显然,它正在做以下工作:

  • 将两个浮点数加载到寄存器xmm0和xmm1上
  • 把它们放在书堆里
  • 将堆栈中的一个值(不是xmm0之前的值)加载到xmm0
  • 执行添加
  • 将结果存储回堆栈
  • 我发现它效率低下,因为:

  • 一切都可以在注册表中完成。稍后我不会使用a和b,因此它可以跳过涉及堆栈的任何操作
  • 即使它想要使用堆栈,如果它以不同的顺序执行操作,它也可以从堆栈中保存重新加载xmm0
  • 既然编译器总是正确的,为什么它会选择这种策略呢?

    -O0
    (未优化)是默认值。它告诉编译器您希望它快速编译(短编译时间),而不是花费额外的编译时间来生成高效的代码

    -O0
    实际上并不是没有优化;例如,如果(1==2){块。尤其是gcc,与大多数其他编译器相比,它仍然会在
    -O0
    处使用乘法逆进行除法,因为它在最终发出asm之前,仍然会通过逻辑的多个内部表示来转换C源代码。)

    另外,“编译器总是正确的”甚至在
    -O3
    上也是一种夸张。编译器在大规模上非常优秀,但在单循环中,遗漏的小优化仍然很常见。循环中的指令(或UOP)通常影响很小,但被浪费,这会占用无序执行重新排序窗口中的空间,并且在与另一个线程共享内核时,对超线程的友好性会降低。有关在简单特定情况下击败编译器的更多信息,请参阅


    更重要的是,
    -O0
    还意味着处理与
    volatile
    类似的所有变量,以便进行一致的调试。i、 因此,您可以设置断点或单步并修改C变量的值,然后继续执行,并使程序按照您希望在C抽象机上运行的C源代码的方式工作。因此编译器不能进行任何常量传播或值范围简化。(例如,已知为非负的整数可以使用它简化事情,或者使某些if条件始终为真或始终为假。)

    (这并不像
    volatile
    那么糟糕:在一条语句中多次引用同一个变量并不总是导致多次加载;在
    -O0
    中,编译器仍然会在单个表达式中进行一些优化。)

    编译器必须专门针对
    -O0
    进行反优化,方法是在语句之间存储/重新加载所有变量到它们的内存地址。(C和C++中,每个变量都有一个地址,除非它被声明为(现在过时)<代码>登记器< /Cord>关键字,并且从未有过地址。根据其他变量的IAS规则,优化地址是可能的,但是在<代码> -O0<代码> < < 不幸的是,调试信息格式无法通过寄存器跟踪变量的位置,因此如果没有这种缓慢而愚蠢的代码生成,就不可能进行完全一致的调试

    如果不需要,可以使用
    -Og
    进行编译以进行轻度优化,而无需进行一致调试所需的反优化。GCC手册建议在通常的编辑/编译/运行周期中使用它,但在调试时,您将对许多具有自动存储功能的局部变量进行“优化”。全局变量和函数参数通常仍然有它们的实际值,至少在函数边界上是这样


    更糟糕的是,
    -O0
    使得即使使用GDB的
    jump
    命令在不同的源代码行继续执行,代码仍然可以工作。因此,每个C语句都必须编译成完全独立的指令块。()

    for()
    循环无法转换为,以及其他限制

    基于以上所有原因,(微观)基准测试未优化的代码是一种巨大的时间浪费;结果取决于如何编写源代码的愚蠢细节,而这些细节在使用常规优化编译时并不重要
    -O0
    -O3
    性能之间没有线性关系;有些代码的速度比其他代码快得多

    -O0
    代码中的瓶颈通常不同于
    -O3
    ——通常在内存中保存的循环计数器上,从而创建一个~6个循环的循环携带依赖链。这可以在编译器生成的asm中创建有趣的效果,比如(从asm的角度来看,这很有趣,但对于C来说不是。)

    “否则我的基准优化了”不是查看
    -O0
    代码性能的有效理由。 有关调整
    -O0
    的兔子洞的示例和更多详细信息,请参见


    获取有趣的编译器输出 如果您想了解编译器如何添加2个变量,write
    int main() {
        float a=0.151234;
        float b=0.2;
        float c=a+b;
        printf("%f", c);
    }
    
    ->  0x100000f30 <+0>:  pushq  %rbp
        0x100000f31 <+1>:  movq   %rsp, %rbp
        0x100000f34 <+4>:  subq   $0x10, %rsp
        0x100000f38 <+8>:  leaq   0x6d(%rip), %rdi       
        0x100000f3f <+15>: movss  0x5d(%rip), %xmm0           
        0x100000f47 <+23>: movss  0x59(%rip), %xmm1        
        0x100000f4f <+31>: movss  %xmm1, -0x4(%rbp)  
        0x100000f54 <+36>: movss  %xmm0, -0x8(%rbp)
        0x100000f59 <+41>: movss  -0x4(%rbp), %xmm0         
        0x100000f5e <+46>: addss  -0x8(%rbp), %xmm0
        0x100000f63 <+51>: movss  %xmm0, -0xc(%rbp)
        ...
    
    float foo(float a, float b) {
        float c=a+b;
        return c;
    }
    
        addss   xmm0, xmm1
        ret
    
    # clang7.0 -O0  also on Godbolt
    foo:
        push    rbp
        mov     rbp, rsp                  # make a traditional stack frame
        movss   DWORD PTR [rbp-20], xmm0  # spill the register args
        movss   DWORD PTR [rbp-24], xmm1  # into the red zone (below RSP)
    
        movss   xmm0, DWORD PTR [rbp-20]  # a
        addss   xmm0, DWORD PTR [rbp-24]  # +b
        movss   DWORD PTR [rbp-4], xmm0   # store c
    
        movss   xmm0, DWORD PTR [rbp-4]   # return 0
        pop     rbp                       # epilogue
        ret