Ich kenne 5 allgemeine Kategorien, bei denen die Neukompilierung eines C++ 03-Compilers als C++ 11 unbegrenzte Leistungssteigerungen verursachen kann, die praktisch nichts mit der Qualität der Implementierung zu tun haben. Dies sind alle Variationen der Bewegungssemantik.
std::vector
umverteilen
struct bar{
std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03
jedes Mal die foo
‚s Puffer umverteilt wird in C++ 03 es jedes vector
in bar
kopiert.
In C++ 11 verschiebt es stattdessen die bar::data
s, die im Grunde frei ist.
In diesem Fall beruht dies auf Optimierungen innerhalb des std
Containers vector
. In jedem Fall unten ist die Verwendung von std
Containern nur, weil sie C++ Objekte sind, die eine effiziente move
Semantik in C++ 11 "automatisch" haben, wenn Sie Ihren Compiler aktualisieren. Objekte, die es nicht blockieren, die einen std
Container enthalten, erben auch die automatisch verbesserten move
-Konstruktoren.
NRVO Ausfall
Wenn NRVO (return genannt Wertoptimierung) ausfällt, in C++ 03 es wieder auf Kopie fällt, auf C 11 ++ es wieder Bewegung fällt. Ausfälle von NRVO sind einfach:
std::vector<int> foo(int count){
std::vector<int> v; // oops
if (count<=0) return std::vector<int>();
v.reserve(count);
for(int i=0;i<count;++i)
v.push_back(i);
return v;
}
oder sogar:
std::vector<int> foo(bool which) {
std::vector<int> a, b;
// do work, filling a and b, using the other for calculations
if (which)
return a;
else
return b;
}
Wir haben drei Werte - den Rückgabewert, und zwei verschiedene Werte innerhalb der Funktion. Elision ermöglicht, dass die Werte innerhalb der Funktion mit dem Rückgabewert "zusammengeführt" werden, aber nicht miteinander. Beide können nicht mit dem Rückgabewert zusammengeführt werden, ohne miteinander zu verschmelzen.
Das grundlegende Problem ist, dass NRVO Elision ist zerbrechlich, und Code mit Änderungen nicht in der Nähe der return
Website kann plötzlich massive Leistungsreduzierungen an dieser Stelle ohne Diagnose emittiert haben. In den meisten NRVO-Fehlerfällen endet C++ 11 mit einer move
, während C++ 03 mit einer Kopie endet.
Rückgabe eines Funktionsargument
Elision ist auch hier unmöglich:
std::set<int> func(std::set<int> in){
return in;
}
in C++ 11 dieses billig ist: in C++ 03 gibt es keine Möglichkeit, die Kopie zu vermeiden. Argumente für Funktionen können nicht mit dem Rückgabewert verknüpft werden, da die Lebensdauer und die Position des Parameters und des Rückgabewerts vom aufrufenden Code verwaltet werden.
Allerdings kann C++ 11 von einem zum anderen wechseln. (In einem weniger Spielzeugbeispiel könnte etwas an der set
getan werden).
push_back
oder insert
Schließlich elision in Behälter geschieht nicht aber C++ 11 Überlastungen rvalue bewegen Einsatz Operatoren, die Kopien spart.
struct whatever {
std::string data;
int count;
whatever(std::string d, int c):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back(whatever("some long string goes here", 3));
in C++ 03 eine temporäre whatever
erstellt wird, dann wird es in den Vektor v
kopiert. 2 std::string
Puffer werden zugeordnet, jeder mit identischen Daten und einer wird verworfen. In C++ 11 wird eine temporäre whatever
erstellt. Die whatever&&
push_back
Überlastung dann move
s, dass temporäre in den Vektor v
. Ein std::string
Puffer wird zugewiesen und in den Vektor verschoben. Ein leerer std::string
wird verworfen.
Zuordnung
Stolen von @ Jarod42 Antwort unten.
Elision kann nicht mit Zuweisung auftreten, aber move-from kann.
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
hier some_function
gibt einen Kandidaten aus elide, sondern weil es nicht verwendet wird, um ein Objekt direkt zu konstruieren, ist es nicht elided werden kann. In C++ 03 führt das obige dazu, dass der Inhalt des temporären Objekts in some_value
kopiert wird. In C++ 11 wird es in some_value
verschoben, was grundsätzlich frei ist.
Für die volle Wirkung der oben genannten, müssen Sie einen Compiler, der für Sie unterwegs und Zuweisungs synthetisiert.
MSVC 2013 implementiert Move-Konstruktoren in std
Containern, jedoch nicht Move-Konstruktoren für Ihre Typen.
Also Typen, die std::vector
s und ähnliche enthalten, erhalten solche Verbesserungen in MSVC2013 nicht, aber beginnen, sie in MSVC2015 zu erhalten.
clang und gcc haben seit langem implizite Move-Konstruktoren implementiert.Intels 2013-Compiler unterstützt die implizite Generierung von Move-Konstruktoren, wenn Sie -Qoption,cpp,--gen_move_operations
übergeben (sie tun dies standardmäßig nicht, um mit MSVC2013 kompatibel zu sein).
Denken Sie daran, dass beim Erstellen eines neuen Objekts mit einem Kopierkonstruktor die Elisions- und Rückgabewertoptimierung durchgeführt wird. In einem Kopierzuweisungsoperator gibt es jedoch keine Kopiereliminierung (wie kann es sein, da der Compiler nicht weiß, was mit einem bereits konstruierten Objekt geschehen soll, das kein temporäres Objekt ist). Daher gewinnt C++ 11/14 in diesem Fall, indem es Ihnen die Möglichkeit gibt, einen Verschiebezuweisungsoperator zu verwenden. Über Ihre Frage denke ich jedoch nicht, dass C++ 98-Code schneller sein sollte, wenn er von einem C++ 11/14-Compiler kompiliert wird, vielleicht ist er schneller, weil der Compiler neuer ist. – vsoftco
Auch Code, der die Standardbibliothek verwendet, ist potenziell schneller, selbst wenn Sie ihn vollständig kompatibel mit C++ 98 machen, da in C++ 11/14 die zugrunde liegende Bibliothek, wenn möglich, eine interne Semantik verwendet. So wird Code, der in C++ 98 und C++ 11/14 identisch aussieht, im letzteren Fall (möglicherweise) schneller, wenn Sie die Standard-Bibliotheksobjekte wie Vektoren, Listen usw. verwenden und die Verschiebungssemantik einen Unterschied macht. – vsoftco
@vsoftco, Das ist die Art von Situation, auf die ich anspielte, aber ich konnte kein Beispiel finden: Von dem, an was ich mich erinnere, wenn ich den Kopierkonstruktor definieren muss, wird der Move-Konstruktor nicht automatisch generiert mit sehr einfachen Klassen, in denen RVO, glaube ich, immer funktioniert. Eine Ausnahme könnte in Verbindung mit den STL-Containern stehen, wo die rvalue-Konstruktoren vom Bibliotheksimplementierer erzeugt werden (was bedeutet, dass ich im Code nichts ändern muss, um Moves zu verwenden). – alarge