Gibt es für die Intel-Architekturen eine Möglichkeit, den GCC-Compiler anzuweisen, Code zu generieren, der die Verzweigungsvorhersage in meinem Code immer auf eine bestimmte Weise erzwingt? Unterstützt die Intel-Hardware dies überhaupt? Was ist mit anderen Compilern oder Hardware?
Ich würde dies in C ++ - Code verwenden, wo ich den Fall kenne, in dem ich schnell laufen möchte, und mich nicht um die Verlangsamung kümmern, wenn der andere Zweig genommen werden muss, selbst wenn er kürzlich diesen Zweig genommen hat.
for (;;) {
if (normal) { // How to tell compiler to always branch predict true value?
doSomethingNormal();
} else {
exceptionalCase();
}
}
Kann der Hinweis als Folgefrage für Evdzhan Mustafa nur einen Hinweis angeben, wenn der Prozessor zum ersten Mal auf die Anweisung trifft, wobei alle nachfolgenden Verzweigungsvorhersagen normal funktionieren?
Antworten:
Ab C ++ 20 sollten die wahrscheinlichen und unwahrscheinlichen Attribute standardisiert sein und werden bereits in g ++ 9 unterstützt . Wie hier beschrieben , können Sie also schreiben
zB im folgenden Code wird der else-Block dank des
[[unlikely]]
im if-Block eingefügtGodbolt-Link zum Vergleich der Anwesenheit / Abwesenheit des Attributs
quelle
[[unlikely]]
inif
vs[[likely]]
in der verwendenelse
?GCC unterstützt die Funktion
__builtin_expect(long exp, long c)
, um diese Art von Funktion bereitzustellen. Sie können die Dokumentation hier überprüfen .Wo
exp
wird die Bedingung verwendet undc
ist der erwartete Wert. Zum Beispiel in Ihrem Fall möchten SieAufgrund der umständlichen Syntax wird dies normalerweise verwendet, indem zwei benutzerdefinierte Makros wie definiert werden
nur um die Aufgabe zu erleichtern.
Beachten Sie, dass:
quelle
constexpr
Funktion anzeigen?constexpr
Funktion dieses Makro ersetzen kann . Es muss in derif
Aussage direkt sein, glaube ich. Der gleiche Grundassert
könnte niemals eineconstexpr
Funktion sein.constexpr
spricht nur über Wertesemantik, nicht über das Inlining implementierungsspezifischer Assemblys); Die einfache Interpretation (kein Inline) des Codes ist bedeutungslos. Es gibt überhaupt keinen Grund, eine Funktion dafür zu verwenden.__builtin_expect
sich um einen Optimierungshinweis handelt. Daher ist es nicht überzeugend zu argumentieren, dass eine Methode, die ihre Verwendung vereinfacht, von der Optimierung abhängt. Außerdem habe ich denconstexpr
Bezeichner nicht hinzugefügt , damit er überhaupt funktioniert, sondern damit er in konstanten Ausdrücken funktioniert. Und ja, es gibt Gründe, eine Funktion zu verwenden. Zum Beispiel möchte ich nicht meinen gesamten Namespace mit einem niedlichen kleinen Namen wie verschmutzenlikely
. Ich müsste zB verwendenLIKELY
, um zu betonen, dass es sich um ein Makro handelt, und Kollisionen vermeiden, aber das ist einfach hässlich.gcc hat long __builtin_expect (long exp, long c) ( Hervorhebung von mir ):
Wie in der Dokumentation erwähnt, sollten Sie lieber das tatsächliche Profilfeedback verwenden. Dieser Artikel zeigt ein praktisches Beispiel dafür und wie es in ihrem Fall zumindest zu einer Verbesserung gegenüber der Verwendung kommt
__builtin_expect
. Siehe auch Verwenden von profilgesteuerten Optimierungen in g ++. .Wir können auch einen Artikel über Linux-Kernel-Neulinge zu den Kernal-Makros wahrscheinlich () und unwahrscheinlich () finden, die diese Funktion verwenden:
Beachten Sie die
!!
im Makro verwendeten. Die Erklärung hierfür finden Sie unter Warum verwenden Sie !! (Bedingung) anstelle von (Bedingung)? .Nur weil diese Technik im Linux-Kernel verwendet wird, bedeutet dies nicht, dass es immer sinnvoll ist, sie zu verwenden. Aus dieser Frage können wir ersehen, dass ich kürzlich den Unterschied zwischen der Funktionsleistung beim Übergeben von Parametern als Kompilierungszeitkonstante oder -variable beantwortet habe , dass viele handgerollte Optimierungstechniken im allgemeinen Fall nicht funktionieren. Wir müssen den Code sorgfältig profilieren, um zu verstehen, ob eine Technik effektiv ist. Viele alte Techniken sind bei modernen Compiler-Optimierungen möglicherweise nicht einmal relevant.
Hinweis: Obwohl Builtins nicht portabel sind, unterstützt Clang auch __builtin_expect .
Auch bei einigen Architekturen macht es möglicherweise keinen Unterschied .
quelle
Nein, da ist kein. (Zumindest auf modernen x86-Prozessoren.)
__builtin_expect
Die in anderen Antworten erwähnten Faktoren beeinflussen die Art und Weise, wie gcc den Assembler-Code anordnet. Es hat keinen direkten Einfluss auf den Verzweigungsprädiktor der CPU. Natürlich wird es indirekte Auswirkungen auf die Verzweigungsvorhersage geben, die durch die Neuordnung des Codes verursacht werden. Auf modernen x86-Prozessoren gibt es jedoch keine Anweisung, die der CPU mitteilt, dass "angenommen wird, dass dieser Zweig verwendet wird / nicht".Weitere Informationen finden Sie in dieser Frage: Intel x86 0x2E / 0x3E Präfix Branch Prediction wird tatsächlich verwendet?
Um klar zu sein
__builtin_expect
und / oder die Verwendung von-fprofile-arcs
kann die Leistung Ihres Codes verbessern, indem Sie dem Verzweigungsprädiktor durch das Codelayout Hinweise geben (siehe Leistungsoptimierungen der x86-64-Assembly - Ausrichtung und Verzweigungsvorhersage ) und das Cache-Verhalten verbessern indem "unwahrscheinlicher" Code von "wahrscheinlichem" Code ferngehalten wird.quelle
__builtin_expect
.__builtin_expect
. Das sollte also nur ein Kommentar sein. Aber es ist nicht falsch, also habe ich meine Ablehnung entfernt.__builtin_expect
trivial einen Testfall erstellen, mit dem Sie messen könnenperf stat
, der eine sehr hohe Rate von Verzweigungsfehlvorhersagen aufweist. Es wirkt sich nur auf das Zweiglayout aus . Und übrigens, Intel verwendet seit Sandybridge oder zumindest Haswell nicht viel / überhaupt keine statische Vorhersage; Es gibt immer eine Vorhersage in der BHT, ob es sich um einen veralteten Alias handelt oder nicht. xania.org/201602/bpu-part-twoDer richtige Weg, um wahrscheinliche / unwahrscheinliche Makros in C ++ 11 zu definieren, ist folgender:
Diese Methode ist im Gegensatz zu allen C ++ - Versionen kompatibel, basiert
[[likely]]
jedoch auf einer nicht standardmäßigen Erweiterung__builtin_expect
.Wenn diese Makros folgendermaßen definiert wurden:
Dies kann die Bedeutung von
if
Anweisungen ändern und den Code beschädigen. Betrachten Sie den folgenden Code:Und seine Ausgabe:
Wie Sie sehen können, bricht die Definition von LIKELY
!!
als Besetzung, umbool
die Semantik von zu brechenif
.Der Punkt hier ist nicht das
operator int()
undoperator bool()
sollte verwandt sein. Welches ist eine gute Praxis.Vielmehr verliert die Verwendung
!!(x)
anstelle vonstatic_cast<bool>(x)
den Kontext für kontextbezogene C ++ 11-Konvertierungen .quelle
switch
, danke. Die hier involvierte kontextbezogene Konvertierung ist partucluar to typebool
und die fünf dort aufgeführten spezifischen Kontexte , die keinenswitch
Kontext enthalten.(_Bool)(condition)
, da C keine Operatorüberladung aufweist.(condition)
, nicht!!(condition)
. Beide sindtrue
nach dem Ändern (getestet mit g ++ 7.1). Können Sie ein Beispiel!!
erstellen, das das Problem demonstriert, über das Sie beim Booleschen Vorgang sprechen?Wie die anderen Antworten alle angemessen vorgeschlagen haben, können Sie
__builtin_expect
dem Compiler einen Hinweis geben, wie der Assemblycode angeordnet werden soll. Wie die offiziellen Dokumente hervorheben , ist der in Ihr Gehirn eingebaute Assembler in den meisten Fällen nicht so gut wie der vom GCC-Team erstellte. Es ist immer am besten, tatsächliche Profildaten zu verwenden, um Ihren Code zu optimieren, anstatt zu raten.In ähnlicher Weise, aber noch nicht erwähnt, ist eine GCC-spezifische Methode, um den Compiler zu zwingen, Code auf einem "kalten" Pfad zu generieren. Dies beinhaltet die Verwendung der Attribute
noinline
undcold
, die genau das tun, wie sie klingen. Diese Attribute können nur auf Funktionen angewendet werden. Mit C ++ 11 können Sie jedoch Inline-Lambda-Funktionen deklarieren, und diese beiden Attribute können auch auf Lambda-Funktionen angewendet werden.Obwohl dies immer noch in die allgemeine Kategorie einer Mikrooptimierung fällt und daher die Standardempfehlung gilt - Test nicht erraten -, halte ich es für allgemeiner nützlich als
__builtin_expect
. Kaum eine Generation des x86-Prozessors verwendet Hinweise zur Verzweigungsvorhersage ( Referenz ). Das einzige, was Sie ohnehin beeinflussen können, ist die Reihenfolge des Assembler-Codes. Da Sie wissen, was Fehlerbehandlungs- oder "Edge-Case" -Code ist, können Sie diese Anmerkung verwenden, um sicherzustellen, dass der Compiler niemals einen Zweig dazu vorhersagt und ihn bei der Größenoptimierung vom "heißen" Code weg verknüpft.Beispielnutzung:
Noch besser ist, dass GCC dies automatisch zugunsten von Profil-Feedback ignoriert, wenn es verfügbar ist (z. B. beim Kompilieren mit
-fprofile-use
).Die offizielle Dokumentation finden Sie hier: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
quelle
__builtin_expect
Fall. Es ist überhaupt nicht nutzlos. Sie haben Recht, dass dascold
Attribut auch nützlich ist, aber Sie unterschätzen den Nutzen von__builtin_expect
Ich denke.__builtin_expect kann verwendet werden, um dem Compiler mitzuteilen, in welche Richtung ein Zweig gehen soll. Dies kann Einfluss darauf haben, wie der Code generiert wird. Typische Prozessoren führen Code nacheinander schneller aus. Also wenn du schreibst
Der Compiler generiert Code wie
Wenn Ihr Hinweis korrekt ist, wird der Code ausgeführt, ohne dass tatsächlich Verzweigungen ausgeführt werden. Es wird schneller als die normale Sequenz ausgeführt, wobei jede if-Anweisung um den bedingten Code verzweigt und drei Verzweigungen ausführt.
Neuere x86-Prozessoren verfügen über Anweisungen für Zweige, von denen erwartet wird, dass sie genommen werden, oder für Zweige, von denen erwartet wird, dass sie nicht genommen werden (es gibt ein Befehlspräfix; die Details sind nicht sicher). Ich bin mir nicht sicher, ob der Prozessor das verwendet. Dies ist nicht sehr nützlich, da die Verzweigungsvorhersage dies problemlos handhaben kann. Also ich glaube nicht , dass Sie tatsächlich den Zweig beeinflussen können Vorhersage .
quelle
In Bezug auf das OP gibt es in GCC keine Möglichkeit, den Prozessor anzuweisen, immer anzunehmen, dass die Verzweigung genommen wird oder nicht. Was Sie haben, ist __builtin_expect, das tut, was andere sagen. Außerdem denke ich, dass Sie dem Prozessor nicht immer mitteilen möchten, ob der Zweig belegt ist oder nicht . Heutige Prozessoren wie die Intel-Architektur können ziemlich komplexe Muster erkennen und sich effektiv anpassen.
Es gibt jedoch Situationen, in denen Sie die Kontrolle darüber übernehmen möchten, ob standardmäßig eine Verzweigung angenommen wird oder nicht: Wenn Sie wissen, dass der Code in Bezug auf Verzweigungsstatistiken als "kalt" bezeichnet wird.
Ein konkretes Beispiel: Ausnahmeverwaltungscode. Per Definition tritt der Verwaltungscode in Ausnahmefällen auf, aber wenn er auftritt, ist möglicherweise maximale Leistung erwünscht (es kann ein kritischer Fehler vorliegen, der so schnell wie möglich behoben werden muss). Daher möchten Sie möglicherweise die Standardvorhersage steuern.
Ein weiteres Beispiel: Sie können Ihre Eingabe klassifizieren und in den Code springen, der das Ergebnis Ihrer Klassifizierung verarbeitet. Wenn es viele Klassifizierungen gibt, kann der Prozessor Statistiken sammeln, diese jedoch verlieren, da dieselbe Klassifizierung nicht früh genug erfolgt und die Vorhersageressourcen für kürzlich aufgerufenen Code verwendet werden. Ich wünschte, es gäbe ein Grundelement, das dem Prozessor mitteilt, "bitte widmen Sie diesem Code keine Vorhersageressourcen", wie Sie manchmal sagen können, "dies nicht zwischenspeichern".
quelle