Wie optimiert GCC eine nicht verwendete Variable, die in einer Schleife inkrementiert wird?

 C Programming >> C-Programmierung >  >> Tags >> GCC
Wie optimiert GCC eine nicht verwendete Variable, die in einer Schleife inkrementiert wird?


Ich habe dieses einfache C-Programm geschrieben:


int main() {
int i;
int count = 0;
for(i = 0; i < 2000000000; i++){
count = count + 1;
}
}

Ich wollte sehen, wie der gcc-Compiler diese Schleife optimiert (eindeutig 1 hinzufügen 2000000000 mal sollte "add 2000000000 sein einmalig"). Also:


gcc-test.c und dann time am a.out ergibt:


real 0m7.717s  
user 0m7.710s
sys 0m0.000s

$ gcc -O2 test.c und dann time on a.out` ergibt:


real 0m0.003s  
user 0m0.000s
sys 0m0.000s

Dann habe ich beides mit gcc -S zerlegt . Das erste scheint ziemlich klar zu sein:


    .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

L3 fügt hinzu, L2 vergleicht -4(%rbp) mit 1999999999 und Schleife zu L3, wenn i < 2000000000 .


Jetzt das Optimierte:


    .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

Ich kann überhaupt nicht verstehen, was da los ist! Ich habe wenig Ahnung von Montage, aber ich habe so etwas wie

erwartet
addl $2000000000, -8(%rbp)

Ich habe es sogar mit gcc -c -g -Wa,-a,-ad -O2 test.c versucht um den C-Code zusammen mit der Assembly zu sehen, in die er konvertiert wurde, aber das Ergebnis war nicht klarer als das vorherige.


Kann jemand kurz erklären:



  1. Das gcc -S -O2 Ausgabe.

  2. Ist die Schleife so optimiert, wie ich es erwartet habe (eine Summe statt vieler Summen)?


Antworten:


Der Compiler ist sogar noch intelligenter. :)


Tatsächlich erkennt es, dass Sie das Ergebnis der Schleife nicht verwenden. Es hat also die gesamte Schleife vollständig entfernt!


Dies wird als Eliminierung toter Codes bezeichnet.


Ein besserer Test ist, das Ergebnis auszudrucken:


#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);
}

BEARBEITEN : Ich habe den erforderlichen #include <stdio.h> hinzugefügt; das MSVC-Assembly-Listing entspricht einer Version ohne den #include , aber es sollte dasselbe sein.



Ich habe GCC im Moment nicht vor mir, da ich in Windows gebootet habe. Aber hier ist die Disassemblierung der Version mit dem printf() auf MSVC:


EDIT:Ich hatte die falsche Assembly-Ausgabe. Hier ist die richtige.


; 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:[email protected]@[email protected]
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

Also ja, Visual Studio führt diese Optimierung durch. Ich würde annehmen, dass GCC das wahrscheinlich auch tut.


Und ja, GCC führt eine ähnliche Optimierung durch. Hier ist ein Assembler-Listing für dasselbe Programm mit 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