it-swarm.com.de

Kann modernes C ++ Sie Leistung für freies erhalten?

Es wird manchmal behauptet, dass C++ 11/14 zu einer Leistungssteigerung führen kann, selbst wenn lediglich C++ 98-Code kompiliert wird. Die Begründung erfolgt in der Regel nach dem Prinzip der Bewegungssemantik, da in einigen Fällen die R-Wert-Konstruktoren automatisch generiert werden oder jetzt Teil der STL sind. Jetzt frage ich mich, ob diese Fälle bisher tatsächlich von RVO oder ähnlichen Compiler-Optimierungen bearbeitet wurden.

Meine Frage ist dann, ob Sie mir ein aktuelles Beispiel für einen Teil des C++ 98-Codes geben könnten, der ohne Änderung schneller ausgeführt wird, wenn ein Compiler verwendet wird, der die neuen Sprachfunktionen unterstützt. Ich verstehe, dass ein standardkonformer Compiler nicht erforderlich ist, um die Kopierentscheidung durchzuführen, und aus diesem Grund könnte die Bewegungssemantik zu einer Geschwindigkeit führen, aber ich würde gerne einen weniger pathologischen Fall sehen, wenn Sie so wollen.

EDIT: Nur um klar zu sein, ich frage nicht, ob neue Compiler schneller sind als alte Compiler, sondern ob es Code gibt, mit dem das Hinzufügen von -std = c ++ 14 zu meinen Compiler-Flags schneller läuft (vermeiden Sie Kopien, aber wenn Sie kann sich außer der Bewegungssemantik noch etwas anderes einfallen lassen, würde mich auch interessieren)

203
alarge

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 alle Variationen der Bewegungssemantik.

std::vector Neu zuweisen

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, wenn der Puffer von foo in C++ 03 neu zugeordnet wird, werden alle vector in bar kopiert.

In C++ 11 werden stattdessen die bar::data Verschoben, was grundsätzlich kostenlos ist.

In diesem Fall hängt dies von Optimierungen im Container stdvector ab. In jedem der folgenden Fälle werden nur std -Container verwendet, weil es sich um C++ - Objekte handelt, die "automatisch" eine effiziente move - Semantik in C++ 11 aufweisen, wenn Sie Ihren Compiler aktualisieren. Objekte, die es nicht blockieren und einen Container std enthalten, erben auch die automatisch verbesserten Konstruktoren move.

NRVO-Fehler

Wenn NRVO (Named Return Value Optimization) fehlschlägt, wird es in C++ 03 beim Kopieren zurückgesetzt, in C++ 11 beim Verschieben. 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 auch:

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. Mit Elision können die Werte in 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 zu verschmelzen.

Das Grundproblem ist, dass die NRVO-Elision zerbrechlich ist und Code mit Änderungen, die sich nicht in der Nähe der return - Site befinden, plötzlich zu massiven Leistungseinbußen an dieser Stelle führen kann, ohne dass eine Diagnose ausgegeben wird. In den meisten NRVO-Fehlerfällen endet C++ 11 mit einem move, während C++ 03 mit einer Kopie endet.

Funktionsargument zurückgeben

Elision ist auch hier nicht möglich:

std::set<int> func(std::set<int> in){
  return in;
}

in C++ 11 ist das billig: In C++ 03 gibt es keine Möglichkeit, die Kopie zu umgehen. Argumente für Funktionen können nicht mit dem Rückgabewert gelöscht werden, da die Lebensdauer und Position des Parameters und des Rückgabewerts vom aufrufenden Code verwaltet wird.

C++ 11 kann jedoch von einem zum anderen wechseln. (In einem Beispiel mit weniger Spielzeug könnte etwas mit set geschehen).

Push_back Oder insert

Schließlich erfolgt keine Aufteilung in Container: C++ 11 überlastet jedoch rvalue move insert -Operatoren, wodurch Kopien gespeichert werden.

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 wird ein temporäres whatever erstellt und dann in den Vektor v kopiert. Es werden 2 std::string - Puffer mit jeweils identischen Daten zugewiesen und einer wird verworfen.

In C++ 11 wird eine temporäre whatever erstellt. Die whatever&&Push_back Überladung setzt dann move die temporäre Überladung in den Vektor v. Ein Puffer std::string Wird zugewiesen und in den Vektor verschoben. Ein leerer std::string Wird verworfen.

Zuordnung

Von @ Jarod42's Antwort unten gestohlen.

Elision kann nicht mit Zuweisung erfolgen, sondern Move-From-Dose.

std::set<int> some_function();

std::set<int> some_value;

// code

some_value = some_function();

hier gibt some_function einen Kandidaten zurück, von dem abgewichen werden soll. Da er jedoch nicht zum direkten Erstellen eines Objekts verwendet wird, kann er nicht abgewichen werden. In C++ 03 führt dies dazu, dass der Inhalt der temporären Datei in some_value Kopiert wird. In C++ 11 wird es in some_value Verschoben, was grundsätzlich kostenlos ist.


Für die volle Wirkung des oben genannten benötigen Sie einen Compiler, der Move-Konstruktoren 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 Und ähnliches enthalten, erhalten in MSVC2013 keine derartigen Verbesserungen, werden jedoch in MSVC2015 eingeführt.

clang und gcc haben längst implizite Move-Konstruktoren implementiert. Intels 2013-Compiler unterstützt die implizite Generierung von Verschiebungskonstruktoren, wenn Sie -Qoption,cpp,--gen_move_operations Übergeben (dies geschieht nicht standardmäßig, um mit MSVC2013 kompatibel zu sein).

220

wenn du etwas hast wie:

std::vector<int> foo(); // function declaration.
std::vector<int> v;

// some code

v = foo();

Sie haben eine Kopie in C++ 03 erhalten, während Sie in C++ 11 eine Verschiebungszuweisung erhalten haben. Sie haben also in diesem Fall freie Optimierung.

45
Jarod42