Ich habe aus verschiedenen Quellen gehört (obwohl meistens von einem Kollegen von mir), dass das Kompilieren mit einer Optimierungsstufe von -O3
in g ++ irgendwie "gefährlich" ist und generell vermieden werden sollte, es sei denn, dies hat sich als notwendig erwiesen.
Ist das wahr und wenn ja, warum? Soll ich mich nur daran halten -O2
?
c++
optimization
g++
compiler-flags
Dunnie
quelle
quelle
-O3
das als besonders fehlerhaft gilt? Ich denke, vielleicht kann es undefiniertes Verhalten "schlimmer" machen, da es seltsame und wundervolle Dinge tun kann, die auf bestimmten Annahmen beruhen, aber das wäre Ihre eigene Schuld. Im Allgemeinen würde ich sagen, dass es in Ordnung ist.-O2
schaltet sich ein-fstrict-aliasing
, und wenn Ihr Code das überlebt, überlebt er wahrscheinlich andere Optimierungen, da die Leute immer wieder falsch liegen. Das heißt,-fpredictive-commoning
ist nur in-O3
und das Aktivieren kann Fehler in Ihrem Code ermöglichen, die durch falsche Annahmen über die Parallelität verursacht werden. Je weniger falsch Ihr Code ist,-Ofast
, es deaktiviert zum Beispiel die IEEE-konforme Handhabung von NaNsAntworten:
In den frühen Tagen von gcc (2.8 usw.) und in den Zeiten von egcs und redhat 2.96 -O3 war es manchmal ziemlich fehlerhaft. Aber das ist über ein Jahrzehnt her und -O3 unterscheidet sich nicht wesentlich von anderen Optimierungsstufen (in Buggyness).
Es werden jedoch tendenziell Fälle aufgedeckt, in denen sich Menschen auf undefiniertes Verhalten verlassen, da sie sich strenger auf die Regeln und insbesondere Eckfälle der Sprache (n) stützen.
Persönlich betreibe ich seit vielen Jahren Produktionssoftware im Finanzsektor mit -O3 und bin noch nicht auf einen Fehler gestoßen, der nicht vorhanden gewesen wäre, wenn ich -O2 verwendet hätte.
Auf vielfachen Wunsch hier eine Ergänzung:
-O3 und insbesondere zusätzliche Flags wie -funroll-Schleifen (nicht von -O3 aktiviert) können manchmal dazu führen, dass mehr Maschinencode generiert wird. Unter bestimmten Umständen (z. B. auf einer CPU mit außergewöhnlich kleinem L1-Befehls-Cache) kann dies zu einer Verlangsamung führen, da der gesamte Code von z. B. einer inneren Schleife jetzt nicht mehr in L1I passt. Im Allgemeinen ist gcc sehr bemüht, nicht so viel Code zu generieren. Da dies jedoch normalerweise den generischen Fall optimiert, kann dies passieren. Besonders anfällige Optionen (wie das Abrollen von Schleifen) sind normalerweise nicht in -O3 enthalten und werden in der Manpage entsprechend gekennzeichnet. Daher ist es im Allgemeinen eine gute Idee, -O3 zum Generieren von schnellem Code zu verwenden und nur dann auf -O2 oder -Os (das versucht, die Codegröße zu optimieren) zurückzugreifen, wenn dies angemessen ist (z. B. wenn ein Profiler L1I-Fehler anzeigt).
Wenn Sie die Optimierung auf das Äußerste bringen möchten, können Sie gcc über --param anpassen, um die mit bestimmten Optimierungen verbundenen Kosten zu ermitteln. Beachten Sie außerdem, dass gcc jetzt die Möglichkeit hat, Funktionen, die die Optimierungseinstellungen nur für diese Funktionen steuern, Attribute zuzuweisen. Wenn Sie also ein Problem mit -O3 in einer Funktion haben (oder spezielle Flags nur für diese Funktion ausprobieren möchten), Sie müssen nicht die gesamte Datei oder das gesamte Projekt mit O2 kompilieren.
otoh es scheint, dass bei der Verwendung von -Ofast Vorsicht geboten ist, in der es heißt:
was mich zu dem Schluss bringt, dass -O3 vollständig standardkonform sein soll.
quelle
std::sort
hilft eine statische Analyse, die nach Aufrufen von Funktionen sucht, wahrscheinlich nicht weiter. Die Verwendung von stackoverflow.com/questions/109710/… würde helfen, oder Sie könnten die Quelle schreiben, um die Sortierung zu nutzen: Scannen Sie, bis Sie> = 128 sehen, und beginnen Sie dann mit der Summierung. Was den aufgeblähten Code betrifft, habe ich vor, ihn zu melden. : PNach meiner etwas wechselhaften Erfahrung wird das Anwenden
-O3
auf ein gesamtes Programm fast immer langsamer (im Vergleich zu-O2
), da es das Abrollen und Inlining aggressiver Schleifen aktiviert, wodurch das Programm nicht mehr in den Anweisungscache passt. Bei größeren Programmen kann dies auch-O2
relativ zu-Os
!Das Verwendungsmuster für
-O3
besteht darin, dass Sie Ihr Programm nach dem Profilieren manuell auf eine kleine Handvoll Dateien anwenden, die kritische innere Schleifen enthalten, die tatsächlich von diesen aggressiven Kompromissen zwischen Platz und Geschwindigkeit profitieren. Neuere Versionen von GCC verfügen über einen profilgesteuerten Optimierungsmodus, mit dem (IIUC) die-O3
Optimierungen selektiv auf Hot-Funktionen angewendet werden können, wodurch dieser Prozess effektiv automatisiert wird.quelle
Die Option -O3 aktiviert zusätzlich zu allen Optimierungen der unteren Ebenen '-O2' und '-O1' teurere Optimierungen wie Funktionsinlining. Die Optimierungsstufe '-O3' kann die Geschwindigkeit der resultierenden ausführbaren Datei erhöhen, aber auch ihre Größe erhöhen. Unter bestimmten Umständen, wenn diese Optimierungen nicht günstig sind, kann diese Option ein Programm tatsächlich verlangsamen.
quelle
Ja, O3 ist fehlerhafter. Ich bin ein Compiler-Entwickler und habe eindeutige und offensichtliche GCC-Fehler identifiziert, die durch die Erstellung fehlerhafter SIMD-Montageanweisungen durch O3 beim Erstellen meiner eigenen Software verursacht wurden. Soweit ich gesehen habe, werden die meisten Produktionssoftware mit O2 ausgeliefert, was bedeutet, dass O3 beim Testen und bei der Behebung von Fehlern weniger Aufmerksamkeit erhält.
Stellen Sie sich das so vor: O3 fügt mehr Transformationen zu O2 hinzu, wodurch mehr Transformationen zu O1 hinzugefügt werden. Statistisch gesehen bedeuten mehr Transformationen mehr Fehler. Das gilt für jeden Compiler.
quelle
Vor kurzem hatte ich ein Problem mit der Optimierung mit
g++
. Das Problem hing mit einer PCI-Karte zusammen, bei der die Register (für Befehle und Daten) durch eine Speicheradresse dargestellt wurden. Mein Treiber hat die physische Adresse einem Zeiger in der Anwendung zugeordnet und sie dem aufgerufenen Prozess übergeben, der wie folgt damit funktioniert hat:Die Karte hat nicht wie erwartet funktioniert. Als ich die Versammlung sah verstand ich , dass der Compiler nur schrieb
someCommand[ the last ]
inpciMemory
alle vorhergehenden Schreibvorgänge weggelassen.Fazit: Seien Sie genau und aufmerksam bei der Optimierung.
quelle
pciMemory
als deklarierenvolatile
.pciMemory
da alle anderen Schreibvorgänge nachweislich keine Wirkung haben. Für den Optimierer ist das großartig, weil er viele nutzlose und zeitaufwändige Anweisungen entfernen kann.