2010-09-13 17 views
36

Gibt es irgendwelche tragbaren Weg, Verzweigungsvorhersage Hinweise zu tun? Betrachten Sie das folgende Beispiel:Portable Branch Prädiktion Hinweise

if (unlikely_condition) { 
    /* ..A.. */ 
    } else { 
    /* ..B.. */ 
    } 

Ist das anders als zu tun:

if (!unlikely_condition) { 
    /* ..B.. */ 
    } else { 
    /* ..A.. */ 
    } 

Oder ist der einzige Weg, Compiler spezifische Hinweise zu benutzen? (z. B. __builtin_expect auf GCC)

Werden Compiler die if Bedingungen anders auf der Grundlage der Reihenfolge der Bedingungen behandeln?

+0

Ich frage mich, ob das etwas C++ 0x Attribute sein könnte, um Bedingungen für 'if' festzuhalten? Wie 'if ([[unwahrscheinlicher]] unwahrscheinlicher_Zustand) {...}'? Zur Zeit erlaubt die Syntax das nicht. Es erlaubt jedoch 'if ([[unwahrscheinliche]] bool b = ...) {}'. Vielleicht könnte man das missbrauchen :) –

+4

GNU-Code enthält eine lächerliche Menge von 'if (wahrscheinlich (...))' Junk in völlig nicht-Performance-kritischen Code, und IMO das ist wirklich schlecht. Zum einen liest es sich nicht natürlich auf Englisch - es klingt wie "wenn diese Bedingung wahrscheinlich wahr ist" statt "wenn diese Bedingung wahr ist, was es wahrscheinlich ist". Und zum anderen ist es nur Unordnung. Wenn Sie nicht viele leistungskritische Bedingungen haben, die nicht bereits nach 'cmov' oder ähnlichem kompilieren, ignorieren Sie einfach Verzweigungsvorhersage-Hinweise. –

+0

@R .. Ich denke ich verstehe, warum der Linux-Kernel mit 'if (unwahrscheinlicher (...)) 'übersät ist. Sie bevorzugen frühe Exits, die den Codefluss leichter nachvollziehen lassen. Wenn sie dies nicht tun würden, würde die statische Verzweigungsvorhersage immer fehlschlagen. –

Antwort

25

Die kanonische Weise statische Verzweigungsvorhersage zu tun ist, dass if wird vorhergesagt, nicht-verzweigte (dh jede if Klausel ausgeführt wird, nicht else) und Schleifen und Rückwärts- goto s sind genommen. Geben Sie also nicht den gemeinsamen Fall in else ein, wenn Sie erwarten, dass die statische Vorhersage signifikant ist. Es ist nicht so einfach, sich in einer noch nicht erreichten Schleife fortzubewegen. Ich habe es nie ausprobiert, aber ich nehme an, es sollte ziemlich gut funktionieren, wenn man es in eine else-Klausel steckt.

Viele Compiler unterstützen irgendeine Form von #pragma unroll, aber es wird immer noch notwendig sein, es mit einer Art von #if zu schützen, um andere Compiler zu schützen.

Branch Prädiktion Hinweise können theoretisch eine vollständige Beschreibung, wie ein Flow-Control-Diagramm eines Programms zu transformieren und die grundlegenden Blöcke in ausführbaren Speicher anzuordnen Ausdruck ... so gibt es eine Vielzahl von Dingen auszudrücken, und die meisten werden nicht sehr portabel sein .

Wie von GNU in der Dokumentation für __builtin_expect empfohlen, ist die profilgeführte Optimierung den Hinweisen überlegen und mit weniger Aufwand.

+0

Und VS hat auch PGO, also ist es eine Win-Win-Situation. :) – GManNickG

1

Seien Sie einfach konsistent mit dem, was Sie tun. Ich mag es zu verwenden

if (!(someExpression)) 

Aber der Compiler sollte dies gleichermaßen behandeln.

7

Optimierung ist inhärent eine Compiler-Sache, so müssen Sie Compiler-Funktionalität verwenden, um es zu helfen. Die Sprache selbst interessiert sich nicht für Optimierungen.

Also das Beste, was Sie tun können ohne Compiler-spezifische Erweiterungen ist organisieren Sie Ihren Code in einer Weise, wo Ihre Compiler "das Richtige tun" ohne Hilfe. Aber wenn Sie sicher sein wollen, tippen Sie auf Compiler-Erweiterungen. (Sie könnten versuchen, sie hinter dem Vorprozessor abstrahiert, so dass Ihr Code tragbar bleibt.)

+4

Es gibt jedoch viele Präzedenzfälle für die Sprache, die Optimierungshinweise liefern ('inline',' restrict', 'register'). Einige sind aktueller als andere auf modernen Compilern. Es spielt keine Rolle, ob eine bestimmte Implementierung tatsächlich etwas mit ihnen macht: Wenn es eine vernünftige Chance gibt, dass jemand etwas Nützliches tut, dann wäre das ein nettes Feature. Ich rechne damit, dass die statische Verzweigungsvorhersage dieses Kriterium im Allgemeinen nicht erfüllt, daher halte ich es nicht für einen schlechten Ruf, es auszulassen. Es ist ein Urteil über die Vorzüge des Falles, aber nicht ganz "wir kümmern uns nie um Optimierung". –

+0

@Steve: Ja, ich denke, ich ignoriere diese automatisch, also vergesse ich sie. Du hast recht. – GManNickG

+2

Ich glaube "restricte" ist wahrscheinlich eine vorzeitige Optimierung wert. Es wird keinen Schaden anrichten, es kann sehr gut sein, indem es schreckliche Speicher-/Ladeabhängigkeiten verhindert, und wo es anwendet, ist es vermutlich eine dokumentierte Anforderung (wie in "memcpy"), ob sich das in der Quelle widerspiegelt oder nicht. Die anderen (und in C++ 03), stimme ich deinem klingenden "meh" zu :-) –

15

In den meisten Fällen der folgende Code

if (a) 
{ 
    ... 
} 
else 
{ 
    ... 
} 

ist eigentlich

evaluate(A) 

if (!A) 
{ 
    jmp p1 
} 

... code A 

    jmp p2 

p1: 

... code !A 

p2: 

Beachten Sie, dass, wenn A wahr ist, "Code A" ist bereits in der Pipeline. Der Prozessor sieht den Befehl "jmp p2" voraus und lädt p2-Code in die Pipeline.

Wenn A falsch ist, befindet sich der "Code! A" möglicherweise nicht in der Pipeline, daher kann er langsamer sein.

Schlussfolgerungen:

  1. tun, wenn (X), wenn X als wahrscheinlicher ist, X
  2. Versuch A so früh wie möglich zu bewerten, so dass die CPU dynmically die Pipeline optimieren!.

:

evaluate(A) 

do more stuff 

if (A) 
    ... 
+0

hab es geschafft! Bitte bewahren Sie den 'if' Code in geschweiften Klammern auf - auch wenn es sich um eine einzelne Zeile handelt. Es ist sonst etwas verwirrend! – Ayyappa

+0

"versuchen Sie, A so früh wie möglich auszuwerten, damit die CPU die Pipeline dynmisch optimieren kann." - Können Sie bitte einige Hinweise für diese Aussage geben? Ich möchte wissen, wie es geht. – Ayyappa

+0

Viele Informationen hier: http://download.intel.com/design/pentiumii/manuals/24281603.pdf –

1

Was für einen bestimmten Compiler über #ifdef mit der Überprüfung falsch ist und diese Dinge hinter einem benutzerdefinierten Makro versteckt? Sie können #define es auf den einfachen Ausdruck erweitern, wenn Sie keinen Compiler haben, der diese Optimierungshinweise unterstützt. Ich habe kürzlich etwas Ähnliches mit expliziten Cache-Vorabholungen gemacht, die GCC über eine intrinsische Funktion unterstützt.

+0

Ich habe das getan. Bei mehr als zwei oder drei Compilern kann es zu einem echten Durcheinander kommen, wenn verschiedene Compiler unterschiedliche Syntax verwenden. –