2013-09-01 15 views
7

Betrachten Sie das folgende C-Programm:Statische Verzweigungsvorhersage/GCC Optimierung

void bar(); 
void baz(); 

void foo(int a) { 
    if (a) { 
     bar(); 
    } 
    else { 
     baz(); 
    } 
} 

Auf meinen x86-64-basierten Computern, die von GCC mit der -O1 Optimierungsstufe erzeugten Anweisungen geben:

0: sub $0x8,%rsp 
4: test %edi,%edi 
6: je  14 <foo+0x14> 
8: mov $0x0,%eax 
d: callq 12 <foo+0x12> # relocation to bar 
12: jmp 1e <foo+0x1e> 
14: mov $0x0,%eax 
19: callq 1e <foo+0x1e> # relocation to baz 
1e: add $0x8,%rsp 
22: retq 

wohingegen der -freorder Blöcke Optimierungsparameter Zugabe (in -O2 enthalten) in den Code schaltet:

0: sub $0x8,%rsp 
4: test %edi,%edi 
6: jne 17 <foo+0x17> 
8: mov $0x0,%eax 
d: callq 12 <foo+0x12> # relocation to baz 
12: add $0x8,%rsp 
16: retq 
17: mov $0x0,%eax 
1c: callq 21 <foo+0x21> # relocation to bar 
21: add $0x8,%rsp 
25: retq 

was ist hauptsächlich eine Änderung von Sprung gleich zu Sprung nicht gleich. Ich weiß, dass bis zu Pentium 4 die statische Verzweigungsvorhersage auf einem bedingten Vorwärtszweig als vom Prozessor nicht angenommen betrachtet wurde (es scheint, dass die statische Vorhersage auf weiteren Intel-Prozessoren zufällig wurde), daher stelle ich mir vor, dass diese Optimierung damit zu tun hat.

dass Unter der Annahme, und was sich auf die jne optimierte Version, würde es bedeuten, dass die sonst Block in der Tat mehr betrachtet wird wahrscheinlich als die wenn Block im Programmablauf ausgeführt werden.

Aber was genau bedeutet das? Da es keine Annahme auf dem Wert in der Funktion durch den Compiler gibt, beruht diese Wahrscheinlichkeit nur auf den Schriften des Programmierers (wer könnte tatsächlich if (!a) anstelle von if (a) und invertierten Funktionsaufrufen verwendet haben).

Bedeutet dies, dass es als eine gute Praxis betrachtet werden sollte, zu behandeln, wenn bedingte Blöcke als Ausnahmefälle (und nicht die normale Ausführung Fluss)?

Das heißt:

if (!cond) { 
    // exceptional code 
} 
else { 
    // normal continuation 
} 

statt:

if (cond) { 
    // normal continuation 
} 
else { 
    // exceptional code 
} 

(natürlich könnte man return-Anweisung innerhalb relevanten Block lieber mit Eindrückungsgröße zu begrenzen).

Antwort

4

Ich hatte einmal erhebliche Leistungsoptimierung Maßnahmen auf ARM (7,9). Es war einfach C, dumm genug Compiler (SDT AFAIR). Eine der Möglichkeiten, einige CPU-Ressourcen zu sparen, bestand darin, if Zweige zu analysieren und den Zustand if so umzuschreiben, dass der normale Fluss die lineare Anweisungssequenz nicht unterbricht. Dies hatte einen positiven Effekt, sowohl wegen der effizienteren Verwendung des CPU-Vorhersageblocks als auch wegen der effizienteren Cache-Segment-Speicher-Cache-Nutzung.

Ich denke hier sehen wir Optimierung, die sehr nahe ist. Im ersten Codefragment führen beide Zweige zur Unterbrechung der normalen Sequenz (Zeile mit 6 für eine Verzweigung und 12 für eine andere). Im zweiten Fragment sind eine Verzweigungsinstruktion bis zu retq geordnet und eine andere Verzweigungssequenz hat einen Einzelsprung (nicht schlechter als in dem ersten Fragment). Bitte beachten Sie 2 retq Anweisungen.

So wie ich sehe dies nicht die Frage der je oder jne ist, sondern vielmehr darum, Blöcke so Zweige Nachbestellung sind lineare Anweisungen Sequenz mit einer von ihnen ohne jump und vollständige Vorhersage Blockleistung eingegeben gespeichert.

In Bezug auf "warum GCC einen Zweig gegenüber einem anderen bevorzugt" ... Ich sehe in der Dokumentation kann dies Ergebnis der statischen Verzweigung Vorhersage (basierend auf Aufrufen innerhalb der Übersetzungseinheit?). Jedenfalls würde ich empfehlen, mit __builtin_expect zu spielen, um ausführlichere Antwort zu haben.

+0

Ja ... Aber die verschiedenen Rückgabepunkte hätten auch auf einer 'je' Version realisiert werden können. Und GCC tut dies, wenn/sonst Block reordering _consciously_ blockieren: im ursprünglichen Programm, das 'if (a) 'zu' if (! A) 'kompiliert zu genau das Gegenteil ändert: von einer' jne' (nicht optimierten) Version zu a 'je' (verzweigungsoptimierte) Version. Ich kann nicht glauben, dass GCC das ändert, nur um sich über mich lustig zu machen! :) – lledr

+0

Ich denke in jedem Fall sollte __builtin_expect dabei helfen ;-). http://blog.man7.org/2012/10/how-much-do-builtinexpect-likely-and.html –