Wie viel Gebrauch von "wahrscheinlichen" und "unwahrscheinlichen" Makros ist zu viel?

12

Die häufig als likelyund bezeichneten unlikelyMakros helfen dem Compiler zu wissen, ob ifnormalerweise ein eingegeben oder übersprungen wird. Die Verwendung führt zu einigen (eher geringfügigen) Leistungsverbesserungen.

Ich habe vor kurzem angefangen, sie zu verwenden, und ich bin mir nicht sicher, wie oft solche Hinweise verwendet werden sollten. Ich benutze es derzeit mit Fehlerprüfungen if, die in der Regel als gekennzeichnet sind unlikely. Beispielsweise:

mem = malloc(size);
if (unlikely(mem == NULL))
  goto exit_no_mem;

Es scheint in Ordnung zu sein, aber Fehlerprüfungen ifkommen ziemlich oft vor und folglich die Verwendung der genannten Makros.

Meine Frage ist, ist es zu viel zu haben likelyund unlikelyMakros auf jeder Fehlerprüfung if?

Welche anderen Orte werden oft benutzt, wenn wir schon dabei sind?


In meiner jetzigen Verwendung befindet es sich in einer Bibliothek, die eine Abstraktion vom Echtzeit-Subsystem vornimmt, sodass Programme zwischen RTAI, QNX und anderen portierbar werden. Die meisten Funktionen sind jedoch eher klein und rufen direkt eine oder zwei andere Funktionen auf. Viele sind sogar static inlineFunktionen.

Zuallererst ist es also keine Anwendung, die ich profilieren könnte. Es ist nicht sinnvoll, Engpässe zu identifizieren, da es sich um eine Bibliothek und nicht um eine eigenständige Anwendung handelt.

Zweitens ist es wie "Ich weiß, dass dies unwahrscheinlich ist, ich könnte es auch dem Compiler sagen". Ich versuche nicht aktiv, das zu optimieren if.

Shahbaz
quelle
7
stinkt nach Mikrooptimierung für mich ...
Ratschenfreak
2
Für Anwendungscode würde ich sie nur hinzufügen, wenn die Profilerstellung zeigt, dass dieser Code in einem Hot Path verwendet wird.
CodesInChaos
@ James, das sagt einfach likelyund unlikelyexistiert und was sie tun. Ich habe nichts gefunden, was darauf hindeuten würde, wann und wo es am besten ist, sie zu verwenden.
Shahbaz
@Shahbaz "Wenn die Bedingung häufig falsch ist, ist die Ausführung nicht linear. In der Mitte befindet sich ein großer Teil unbenutzten Codes, der nicht nur das L1i aufgrund des Vorablesezugriffs verschmutzt, sondern auch Probleme mit der Verzweigungsvorhersage verursachen kann Die Verzweigungsvorhersage ist falsch. Der bedingte Ausdruck kann sehr ineffizient sein. " Also, enge Schleifen, wo Sie sicherstellen möchten, dass die Anweisungen, die Sie benötigen, im L1i-Cache sind
James

Antworten:

12

Benötigen Sie eine so hohe Leistung, dass Sie bereit sind, Ihren Code damit zu verschmutzen? Es ist eine kleine Optimierung.

  • Läuft der Code in einer engen Schleife?
  • Hat Ihre Anwendung Leistungsprobleme?
  • Haben Sie Ihre Anwendung profiliert und festgestellt, dass diese bestimmte Schleife viel CPU-Zeit kostet?

Wenn Sie nicht yesauf alles oben Genannte antworten können, kümmern Sie sich nicht um solche Dinge.

Bearbeiten: als Antwort auf die Bearbeitung. Selbst wenn Sie kein Profil erstellen können, können Sie in der Regel Hotspots schätzen. Eine von jedem aufgerufene Speicherzuweisungsfunktion ist ein guter Kandidat, zumal für die gesamte Bibliothek nur eine einzige Verwendung des Makros erforderlich ist.

Java-Typ
quelle
1
Um ganz klar zu sein, ich habe dich nicht runtergestimmt. Ihre Antwort beantwortet jedoch meine Frage nicht wirklich. Wollen Sie damit sagen, dass ein (un)likelyMakro selten und nur in extrem leistungskritischem Code verwendet wird? Ist es "schlechte Praxis", es oft zu benutzen oder nur "unnötig"?
Shahbaz
@ Shahbaz Es macht den Code weniger lesbar und die Leistungsoptimierung kann von geringfügigem Gewinn bis geringfügigem Verlust reichen. Letzteres, wenn die Annahme über die Wahrscheinlichkeit entweder falsch war oder sich aufgrund einer späteren Änderung an anderen Teilen des Codes als falsch geändert hat. If sollte niemals verwendet werden, es sei denn, dies wird benötigt.
Peter
3
@Peter: Die Syntax ist zwar zu schlecht, aber Notationen darüber, was wahrscheinlich oder unwahrscheinlich ist, liefern möglicherweise nützliche Informationen für Menschen , die Code lesen. Zum Beispiel if (likely(x==2 || x==3)) doOneThing(); else switch(x) { ... }könnte jemand, der es gesehen hat , beurteilen, dass die Verwendung von a iffür die Werte 2 und 3 durch den Programmierer nicht nur eine Folge davon war, dass der Programmierer nicht wusste, dass C zwei caseLabels mit einem einzigen Handler verknüpfen kann .
Superkatze
Niemand hat erwähnt, was meiner Meinung nach ein kritischer Punkt ist. Es ist nicht nur so, dass der "unwahrscheinliche" Pfad seltener vorkommt, es kann auch sein, dass Sie , bedingt durch diesen Pfad, sich überhaupt nicht für Geschwindigkeit interessieren. Beispielsweise reagiert ein Peripheriegerät nicht mehr, sodass Sie es zurücksetzen und trotzdem in den Energiesparmodus wechseln müssen.
Benjamin Lindqvist
2

Wenn Sie für x86 / x64 schreiben (und keine 20 Jahre alten CPUs verwenden), ist der Leistungsgewinn durch die Verwendung von __builtin_expect () vernachlässigbar. Der Grund dafür ist, dass moderne x86 / x64-CPUs (die sich in Bezug auf Atom nicht 100% sicher sind) eine dynamische Verzweigungsvorhersage haben, sodass die CPU im Wesentlichen von der Verzweigung "erfährt", die häufiger in Anspruch genommen wird. Natürlich können diese Informationen nur für eine begrenzte Anzahl von Zweigen gespeichert werden, es sind jedoch nur zwei Fälle möglich. Wenn (a) es sich um einen "häufig verwendeten" Zweig handelt, profitiert Ihr Programm von dieser dynamischen Verzweigungsvorhersage, und wenn (b) es sich um einen "seltenen" Zweig handelt, werden Sie aufgrund von Fehleinschätzungen in keinen realistischen Leistungseinbruch feststellen Solche seltenen Verzweigungen (20 CPU-Zyklen der Verzweigungsfehlervorhersage sind nicht ZU schlecht, wenn sie einmal in einem blauen Mond vorkommen).

NB: Dies bedeutet NICHT, dass auf modernen x86 / x64-Systemen die Wichtigkeit von Verzweigungsfehlervorhersagen geringer geworden ist: Bei Verzweigungen mit einer Wahrscheinlichkeit von 50-50 von Sprung-Nojump wird immer noch eine Strafe verhängt (IIRC 10-20 CPU-Zyklen) müssen noch vermieden werden. Es ist nur wichtig, dass __builtin_expect () unter x86 / x64 abgenommen hat (IIRC vor etwa 10-15 Jahren) - hauptsächlich aufgrund der dynamischen Verzweigungsvorhersage.

NB2: für andere Plattformen als x86 / x64, YMMV.

No-Bugs-Hase
quelle
Nun, der Compiler weiß, für welchen Zweig die CPU eine geringere Wahrscheinlichkeit erwartet. Und es kann dafür sorgen, dass dies das eigentlich unwahrscheinliche ist. Aber der Compiler kennt dieses Muster wahrscheinlich schon ohne die unlikely-Notation.
Deduplicator
@ Deduplicator: Bei der dynamischen Verzweigungsvorhersage weiß der Compiler nicht, welche Verzweigung wahrscheinlicher ist, da die CPU sie in der Laufzeit anhand der vorherigen Durchläufe genau an dieser Stelle im Code berechnet.
No-Bugs Hare