Betrachten Sie diesen Code:
%Vor% Mit g++ -std=c++14 -pedantic -O3
bekomme ich diese Assembly:
Nach meiner Schätzung sollte die Variable x
mindestens dreimal (möglicherweise vier) geschrieben werden, aber nicht einmal einmal geschrieben (die Funktion foo wird nicht einmal aufgerufen!)
Noch schlimmer, wenn Sie das inline
-Schlüsselwort zu foo
hinzufügen, ist dies das Ergebnis:
Ich dachte, dass flüchtig bedeutet, dass jedes einzelne Lesen oder Schreiben muss passieren, selbst wenn der Compiler den Punkt des Lese- / Schreibzugriffs nicht sehen kann.
Was ist hier los?
Aktualisierung:
Setzen Sie die Deklaration von A a;
outside main wie folgt:
Erzeugt diesen Code:
%Vor%Was näher an dem ist, was Sie erwarten würden .... Ich bin noch mehr verwirrt als je zuvor
Visual C ++ 2015 optimiert die Zuweisungen nicht:
%Vor%Das gleiche passiert sowohl mit / O2 (Geschwindigkeit maximieren) als auch / Ox (vollständige Optimierung).
Die flüchtigen Schreibvorgänge werden auch von gcc 3.4.4 sowohl mit -O2 als auch -O3
beibehalten %Vor%Bei Verwendung dieser beiden Compiler wird main () im Wesentlichen leer, wenn ich das flüchtige Schlüsselwort entferne.
Ich würde sagen, Sie haben einen Fall, in dem der Compiler aggressiv (und fälschlicherweise IMHO) beschließt, dass Operationen nicht notwendig sind, da "a" nicht verwendet wird, und das flüchtige Element nicht berücksichtigt. "A" selbst flüchtig zu machen könnte dir das bringen, was du willst, aber da ich keinen Compiler habe, der das reproduziert, kann ich das nicht sicher sagen.
Last (das ist zugegebenermaßen Microsoft-spezifisch), Ссылка sagt:
Wenn ein Strukturelement als flüchtig markiert ist, wird flüchtig an die gesamte Struktur weitergegeben.
Was auch auf das Verhalten hindeutet, das Sie als Compiler-Problem sehen.
Wenn Sie schließlich 'a' zu einer globalen Variable machen, ist es einigermaßen verständlich, dass der Compiler weniger daran interessiert ist, sie als unbenutzt zu betrachten und sie zu löschen. Globale Variablen sind standardmäßig extern, so dass es nicht möglich ist zu sagen, dass ein globales "a" ungenutzt ist, wenn man nur die Hauptfunktion betrachtet. Eine andere Kompilierungseinheit (.cpp-Datei) könnte sie verwenden.
GCC-Seite auf Flüchtigen Zugriff gibt einige Einblicke in die Funktionsweise:
Der Standard ermutigt Compiler, Optimierungen in Bezug auf Zugriffe auf flüchtige Objekte zu unterlassen, lässt aber die Implementierung als einen flüchtigen Zugriff definiert. Die Mindestanforderung ist, dass sich an einem Sequenzpunkt alle vorherigen Zugriffe auf flüchtige Objekte stabilisiert haben und keine nachfolgenden Zugriffe aufgetreten sind. Somit ist es einer Implementierung freigestellt, flüchtige Zugriffe, die zwischen Sequenzpunkten auftreten, neu zu ordnen und zu kombinieren, kann dies jedoch nicht für Zugriffe über einen Sequenzpunkt hinweg tun. Die Verwendung von volatile erlaubt es Ihnen nicht, die Einschränkung für das mehrfache Aktualisieren von Objekten zwischen zwei Sequenzpunkten zu verletzen.
In C standardese:
§5.1.2.3
2 Zugriff auf ein flüchtiges Objekt, Ändern eines Objekts, Ändern einer Datei, oder rufen Sie eine Funktion auf, die alle diese Operationen alle -Seite hat Effekte , 11) welche Änderungen im Zustand der Ausführungsumgebung. Die Auswertung eines Ausdrucks kann eine Seite erzeugen Auswirkungen. An bestimmten angegebenen Punkten in der Ausführungsreihenfolge aufgerufen Sequenzpunkte müssen alle Nebenwirkungen früherer Bewertungen vollständig sein und keine Nebenwirkungen nachfolgender Auswertungen haben geschehen. (Eine Zusammenfassung der Sequenzpunkte ist in Anhang C angegeben.)
3 In der abstrakten Maschine werden alle Ausdrücke wie angegeben ausgewertet durch die Semantik. Eine tatsächliche Implementierung muss nicht evaluiert werden ein Ausdruck, wenn er folgern kann, dass sein Wert nicht verwendet wird und dass nein Es werden die notwendigen Nebenwirkungen erzeugt (einschließlich aller durch das Aufrufen eines Funktion oder Zugriff auf ein flüchtiges Objekt).
[...]
5 Die geringsten Anforderungen an eine konforme Implementierung sind:
- An flüchtigen Punkten sind flüchtige Objekte in dem Sinne stabil, dass vorherige Zugriffe abgeschlossen sind und nachfolgende Zugriffe noch nicht abgeschlossen sind aufgetreten. [...]
Ich habe den C-Standard gewählt, weil die Sprache einfacher ist, aber die Regeln in C ++ im Wesentlichen gleich sind. Siehe die "Als-ob" -Regel.
Jetzt, auf meinem Rechner, optimiert -O1
den Aufruf von foo()
nicht, also verwenden wir -fdump-tree-optimized
, um den Unterschied zu sehen:
-O1
Und -O3
:
gdb
zeigt in beiden Fällen, dass a
letztendlich optimiert ist, aber wir machen uns Sorgen um foo()
. Die Dumps zeigen uns, dass GCC die Zugriffe neu geordnet hat, so dass foo()
nicht einmal notwendig ist und anschließend der gesamte Code in main()
optimiert ist. Ist das wirklich wahr? Sehen wir uns die Assembly-Ausgabe für -O1
an:
Dies bestätigt im Wesentlichen, was ich oben gesagt habe. Alles ist optimiert: Der einzige Unterschied ist, ob der Aufruf von foo()
auch so ist.
Tags und Links c++ language-lawyer volatile g++