GCC如何优化循环内增加的未使用变量?

Hai*_*ile 64 c gcc disassembly compiler-optimization

我写了这个简单的C程序:

int main() {
    int i;
    int count = 0;
    for(i = 0; i < 2000000000; i++){
        count = count + 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

我想看看gcc编译器如何优化这个循环(显然添加1 2000000000次应该是"一次添加2000000000 ").所以:

GCC test.c的,然后timea.out给出了:

real 0m7.717s  
user 0m7.710s  
sys 0m0.000s  
Run Code Online (Sandbox Code Playgroud)

$ gcc -O2 test.c 然后time ona.out`给出:

real 0m0.003s  
user 0m0.000s  
sys 0m0.000s  
Run Code Online (Sandbox Code Playgroud)

然后我用两个人拆开了gcc -S.第一个似乎很清楚:

    .file "test.c"  
    .text  
.globl main
    .type   main, @function  
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    movq    %rsp, %rbp
    .cfi_offset 6, -16
    .cfi_def_cfa_register 6
    movl    $0, -8(%rbp)
    movl    $0, -4(%rbp)
    jmp .L2
.L3:
    addl    $1, -8(%rbp)
    addl    $1, -4(%rbp)
.L2:
    cmpl    $1999999999, -4(%rbp)
    jle .L3
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
    .section    .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

L3添加,L2 -4(%rbp)与L3比较1999999999并循环到L3 if i < 2000000000.

现在优化的一个:

    .file "test.c"  
    .text
    .p2align 4,,15
.globl main
    .type main, @function
main:
.LFB0:
    .cfi_startproc
    rep
    ret
    .cfi_endproc
.LFE0:
    .size main, .-main
    .ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
    .section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

我根本无法理解那里发生了什么!我对装配知之甚少,但我期待类似的东西

addl $2000000000, -8(%rbp)
Run Code Online (Sandbox Code Playgroud)

我甚至尝试使用gcc -c -g -Wa,-a,-ad -O2 test.c来查看C代码以及它转换为的程序集,但结果不再清楚前一个.

有人能简单解释一下:

  1. GCC -S -O2输出.
  2. 如果循环按照我的预期进行优化(一个总和而不是多个总和)?

Mys*_*ial 73

编译器甚至比这更聪明.:)

实际上,它意识到你没有使用循环的结果.所以它完全取出了整个循环!

这称为死代码消除.

更好的测试是打印结果:

#include <stdio.h>
int main(void) {
    int i; int count = 0;
    for(i = 0; i < 2000000000; i++){
        count = count + 1;
    }

    //  Print result to prevent Dead Code Elimination
    printf("%d\n", count);
}
Run Code Online (Sandbox Code Playgroud)

编辑:我添加了所需#include <stdio.h>; MSVC程序集列表对应于没有的版本#include,但它应该是相同的.


我现在没有GCC在我面前,因为我已经启动进入Windows.但这是使用printf()on MSVC 的版本的反汇编:

编辑:我有错误的汇编输出.这是正确的.

; 57   : int main(){

$LN8:
    sub rsp, 40                 ; 00000028H

; 58   : 
; 59   : 
; 60   :     int i; int count = 0;
; 61   :     for(i = 0; i < 2000000000; i++){
; 62   :         count = count + 1;
; 63   :     }
; 64   : 
; 65   :     //  Print result to prevent Dead Code Elimination
; 66   :     printf("%d\n",count);

    lea rcx, OFFSET FLAT:??_C@_03PMGGPEJJ@?$CFd?6?$AA@
    mov edx, 2000000000             ; 77359400H
    call    QWORD PTR __imp_printf

; 67   : 
; 68   : 
; 69   : 
; 70   :
; 71   :     return 0;

    xor eax, eax

; 72   : }

    add rsp, 40                 ; 00000028H
    ret 0
Run Code Online (Sandbox Code Playgroud)

是的,Visual Studio进行了这种优化.我认为GCC也可能会这样做.

是的,GCC执行类似的优化.这是gcc -S -O2 test.c(gcc 4.5.2,Ubuntu 11.10,x86)相同程序的汇编列表:

        .file   "test.c"
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "%d\n"
        .text
        .p2align 4,,15
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $16, %esp
        movl    $2000000000, 8(%esp)
        movl    $.LC0, 4(%esp)
        movl    $1, (%esp)
        call    __printf_chk
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
        .section        .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

  • 不要觉得愚蠢.如果你刚刚进入微基准测试,这种事情根本不明显.这只是学习过程的一部分. (11认同)
  • @marcushatchenson - 循环的唯一影响是增加`count`,这是一个局部变量.C规范指出函数外部没有任何东西知道本地,并且编译器知道函数不对结果做任何事情.根据规范的规则,如果不计算`count`,则对程序没有可能的影响,因此允许优化器将其丢弃.另一方面,如果将count声明为全局,则编译器必须以不同方式对待它. (10认同)
  • @marcushatchenson这是一个非常重要的编译主题.基本思想是编译器生成[依赖关系图](http://en.wikipedia.org/wiki/Dependency_graph),然后可以用它来证明/反驳是否需要某些计算.然后消除被证明不需要的东西. (4认同)
  • 好吧,我现在感到很蠢.没想到(eew ..不知道)关于死码消除.我尝试使用printf()和gcc,它产生了完全相同的优化代码.谢谢你的回答! (2认同)