Es wird manchmal behauptet, dass C ++ 11/14 Ihnen eine Leistungssteigerung bringen kann, selbst wenn Sie lediglich C ++ 98-Code kompilieren. Die Begründung erfolgt normalerweise in Anlehnung an die Bewegungssemantik, da in einigen Fällen die rvalue-Konstruktoren automatisch generiert werden oder jetzt Teil der STL sind. Jetzt frage ich mich, ob diese Fälle zuvor tatsächlich bereits von RVO oder ähnlichen Compiler-Optimierungen behandelt wurden.
Meine Frage ist dann, ob Sie mir ein aktuelles Beispiel für einen Teil des C ++ 98-Codes geben können, der ohne Änderung mit einem Compiler, der die neuen Sprachfunktionen unterstützt, schneller ausgeführt wird. Ich verstehe, dass ein standardkonformer Compiler nicht erforderlich ist, um die Kopierelision durchzuführen, und nur aus diesem Grund kann die Verschiebungssemantik zu einer Geschwindigkeit führen, aber ich würde gerne einen weniger pathologischen Fall sehen, wenn Sie so wollen.
EDIT: Um ganz klar zu sein, ich frage nicht, ob neue Compiler schneller sind als alte Compiler, sondern ob es Code gibt, durch den das Hinzufügen von -std = c ++ 14 zu meinen Compiler-Flags schneller ausgeführt wird (vermeiden Sie Kopien, aber wenn Sie kann mir außer der Bewegungssemantik noch etwas anderes einfallen lassen, das würde mich auch interessieren)
quelle
std::move
, Konstruktoren hinzuzufügen und zu verschieben (was Änderungen am vorhandenen Code erfordern würde). Das einzige, was wirklich mit meiner Frage zu tun hatte, war der Satz "Sie erhalten sofortige Geschwindigkeitsvorteile, indem Sie ihn einfach neu kompilieren", der nicht durch Beispiele untermauert wird (STL wird auf derselben Folie erwähnt, wie ich es in meiner Frage getan habe, aber nichts Spezifisches ). Ich habe nach einigen Beispielen gefragt. Wenn ich die Folien falsch lese, lassen Sie es mich wissen.Antworten:
Mir sind 5 allgemeine Kategorien bekannt, in denen das Neukompilieren eines C ++ 03-Compilers als C ++ 11 zu unbegrenzten Leistungssteigerungen führen kann, die praktisch nichts mit der Qualität der Implementierung zu tun haben. Dies sind alles Variationen der Bewegungssemantik.
std::vector
neu zuweisenJedes Mal
foo
, wenn der Puffer in C ++ 03 neu zugewiesen wird, wird jedervector
in kopiertbar
.In C ++ 11 wird stattdessen das
bar::data
s verschoben, was grundsätzlich kostenlos ist.In diesem Fall hängt dies von Optimierungen im
std
Container abvector
. In allen folgenden Fällen werdenstd
Container nur verwendet, weil es sich um C ++ - Objekte handelt, diemove
in C ++ 11 "automatisch" eine effiziente Semantik aufweisen, wenn Sie Ihren Compiler aktualisieren. Objekte, die es nicht blockieren und einenstd
Container enthalten, erben auch die automatisch verbessertenmove
Konstruktoren.NRVO-Fehler
Wenn NRVO (Named Return Value Optimization) fehlschlägt, wird in C ++ 03 beim Kopieren zurückgegriffen, in C ++ 11 beim Verschieben. Ausfälle von NRVO sind einfach:
oder auch:
Wir haben drei Werte - den Rückgabewert und zwei verschiedene Werte innerhalb der Funktion. Mit Elision können die Werte innerhalb der Funktion mit dem Rückgabewert 'zusammengeführt' werden, jedoch nicht miteinander. Sie können beide nicht mit dem Rückgabewert zusammengeführt werden, ohne miteinander zusammengeführt zu werden.
Das Grundproblem besteht darin, dass die NRVO-Elision fragil ist und Code mit Änderungen, die sich nicht in der Nähe des
return
Standorts befinden, an dieser Stelle plötzlich massive Leistungseinbußen aufweisen kann, ohne dass eine Diagnose ausgegeben wird. In den meisten NRVO-Fehlerfällen endet C ++ 11 mit amove
, während C ++ 03 mit einer Kopie endet.Rückgabe eines Funktionsarguments
Elision ist auch hier nicht möglich:
In C ++ 11 ist dies billig: In C ++ 03 gibt es keine Möglichkeit, die Kopie zu umgehen. Argumente für Funktionen können nicht mit dem Rückgabewert entfernt werden, da die Lebensdauer und Position des Parameters und des Rückgabewerts vom aufrufenden Code verwaltet werden.
C ++ 11 kann jedoch von einem zum anderen verschoben werden. (In einem weniger spielerischen Beispiel könnte etwas mit dem gemacht werden
set
).push_back
oderinsert
Schließlich findet keine Elision in Container statt: C ++ 11 überlastet jedoch rvalue move insert-Operatoren, wodurch Kopien gespeichert werden.
In C ++ 03 wird ein Temporär
whatever
erstellt und dann in den Vektor kopiertv
. Es werden 2std::string
Puffer mit jeweils identischen Daten zugewiesen und einer verworfen.In C ++ 11 wird eine temporäre
whatever
Datei erstellt. Diewhatever&&
push_back
Überlastung ist dannmove
so temporär in den Vektorv
. Einstd::string
Puffer wird zugewiesen und in den Vektor verschoben. Ein Leerzeichenstd::string
wird verworfen.Zuordnung
Gestohlen aus der Antwort von @ Jarod42 unten.
Elision kann nicht mit Zuweisung erfolgen, sondern mit Abzug.
Hier wird
some_function
ein Kandidat zurückgegeben, von dem entfernt werden soll. Da er jedoch nicht zum direkten Erstellen eines Objekts verwendet wird, kann er nicht entfernt werden. In C ++ 03 führt das Obige dazu, dass der Inhalt des Temporären kopiert wirdsome_value
. In C ++ 11 wird es verschobensome_value
, was grundsätzlich kostenlos ist.Für die volle Wirkung des oben genannten benötigen Sie einen Compiler, der Verschiebungskonstruktoren und Zuweisungen für Sie synthetisiert.
MSVC 2013 implementiert Verschiebungskonstruktoren in
std
Containern, synthetisiert jedoch keine Verschiebungskonstruktoren für Ihre Typen.Typen, die
std::vector
s und ähnliches enthalten, erhalten solche Verbesserungen in MSVC2013 nicht, werden sie jedoch in MSVC2015 erhalten.clang und gcc haben längst implizite Verschiebungskonstruktoren implementiert. Der 2013er Compiler von Intel unterstützt die implizite Generierung von Verschiebungskonstruktoren, wenn Sie übergeben
-Qoption,cpp,--gen_move_operations
(sie tun dies standardmäßig nicht, um mit MSVC2013 kompatibel zu sein).quelle
std
werden alle Bibliothekscontainermove
"kostenlos" mit Konstruktoren aktualisiert und (wenn Sie sie nicht blockiert haben) Konstrukte, die diese Objekte verwenden ( und die genannten Objekte) werden in einer Reihe von Situationen mit dem Bau von freien Bewegungen beginnen. Viele dieser Situationen werden in C ++ 03 durch Elision abgedeckt: nicht alle.std
oben genannten Container ist hauptsächlich darauf zurückzuführen, dass sie billig zu verschieben sind, um den Typ zu kopieren, den Sie in C ++ 11 beim Neukompilieren von C ++ 03 "kostenlos" erhalten. Dasvector::resize
ist eine Ausnahme: Es wirdmove
in C ++ 11 verwendet.wenn du so etwas hast wie:
Sie haben eine Kopie in C ++ 03 erhalten, während Sie in C ++ 11 eine Verschiebungszuweisung erhalten haben. In diesem Fall haben Sie also eine kostenlose Optimierung.
quelle
foo().swap(v);