Ich kann sehen, warum der auto
Typ in C ++ 11 die Korrektheit und Wartbarkeit verbessert. Ich habe gelesen, dass es auch die Leistung verbessern kann ( Fast immer automatisch von Herb Sutter), aber ich vermisse eine gute Erklärung.
- Wie kann
auto
die Leistung verbessert werden? - Kann jemand ein Beispiel geben?
c++
performance
c++11
auto
DaBrain
quelle
quelle
Antworten:
auto
kann die Leistung verbessern, indem stille implizite Konvertierungen vermieden werden . Ein Beispiel, das mich überzeugt, ist das folgende.Sehen Sie den Fehler? Hier denken wir, dass wir jedes Element in der Karte elegant als Konstantenreferenz nehmen und den neuen Bereich-für-Ausdruck verwenden, um unsere Absicht klar zu machen, aber tatsächlich kopieren wir jedes Element. Dies liegt daran
std::map<Key, Val>::value_type
iststd::pair<const Key, Val>
, nichtstd::pair<Key, Val>
. Wenn wir also (implizit) haben:Anstatt einen Verweis auf ein vorhandenes Objekt zu nehmen und es dabei zu belassen, müssen wir eine Typkonvertierung durchführen. Sie dürfen einen konstanten Verweis auf ein Objekt (oder ein temporäres Objekt) eines anderen Typs erstellen, solange eine implizite Konvertierung verfügbar ist, z.
Die Typkonvertierung ist eine zulässige implizite Konvertierung aus demselben Grund, aus dem Sie a
const Key
in a konvertieren können.Key
Wir müssen jedoch eine temporäre Konvertierung des neuen Typs erstellen, um dies zu ermöglichen. Somit macht unsere Schleife effektiv:(Natürlich gibt es eigentlich kein
__tmp
Objekt, es dient nur zur Veranschaulichung, in Wirklichkeit ist das unbenannte Temporär nuritem
für seine Lebensdauer gebunden ).Einfach ändern zu:
Wir haben gerade eine Menge Kopien gespeichert - jetzt stimmt der Typ, auf den verwiesen wird, mit dem Initialisierungstyp überein, sodass keine temporäre oder Konvertierung erforderlich ist. Wir können nur eine direkte Referenz erstellen.
quelle
std::pair<const Key, Val> const &
als zu behandelnstd::pair<Key, Val> const &
? Neu in C ++ 11, nicht sicher, wie weitreichend undauto
spielt dies.auto
, die die Leistung steigern. Also schreibe ich es unten in meinen eigenen Worten auf.auto
die Leistung verbessert" wird. Dies ist nur ein Beispiel dafür, dass "auto
Programmiererfehler vermieden werden, die die Leistung beeinträchtigen". Ich behaupte, dass es einen subtilen, aber wichtigen Unterschied zwischen den beiden gibt. Trotzdem +1.Da
auto
der Typ des initialisierenden Ausdrucks abgeleitet wird, ist keine Typkonvertierung erforderlich. In Kombination mit Vorlagenalgorithmen bedeutet dies, dass Sie eine direktere Berechnung erhalten können, als wenn Sie selbst einen Typ erstellen würden - insbesondere, wenn Sie mit Ausdrücken arbeiten, deren Typ Sie nicht benennen können!Ein typisches Beispiel stammt von (ab) mit
std::function
:Mit
cmp2
undcmp3
kann der gesamte Algorithmus den Vergleichsaufruf inline. Wenn Sie jedoch einstd::function
Objekt erstellen, kann der Aufruf nicht nur nicht inline eingefügt werden, sondern Sie müssen auch die polymorphe Suche im typgelöschten Inneren des Funktionsumschlags durchlaufen.Eine andere Variante dieses Themas ist, dass Sie sagen können:
Dies ist immer eine Referenz, die an den Wert des Funktionsaufrufausdrucks gebunden ist und niemals zusätzliche Objekte erstellt. Wenn Sie den Typ des zurückgegebenen Werts nicht kennen, müssen Sie möglicherweise ein neues Objekt (möglicherweise als temporäres Objekt) über so etwas wie erstellen
T && f = MakeAThing()
. (Funktioniertauto &&
auch dann, wenn der Rückgabetyp nicht beweglich ist und der Rückgabewert ein Wert ist.)quelle
auto
. Ihre andere Variante lautet "Vermeiden Sie versehentliche Kopien", muss jedoch verschönert werden. Warum gibtauto
es Ihnen die Möglichkeit, den Typ dort einfach einzugeben? (Ich denke, die Antwort lautet "Sie verstehen den Typ falsch und er konvertiert stillschweigend") Was macht ihn zu einem weniger gut erklärten Beispiel für Barrys Antwort, nein? Das heißt, es gibt zwei grundlegende Fälle: Auto, um das Löschen von Typen zu vermeiden, und Auto, um unbeabsichtigte Typfehler zu vermeiden, die versehentlich konvertiert werden. Beide haben Laufzeitkosten.std::bind
,std::function
undstd::stable_partition
alle haben inlined worden? Oder nur, dass in der Praxis kein C ++ - Compiler aggressiv genug inline wird, um das Chaos zu beseitigen?std::function
Konstruktor durchlaufen haben , wird es sehr komplex sein, den tatsächlichen Aufruf zu durchschauen, insbesondere bei Optimierungen mit kleinen Funktionen (sodass Sie eigentlich keine Devirtualisierung wünschen). Natürlich ist im Prinzip alles so, als ob ...Es gibt zwei Kategorien.
auto
kann das Löschen des Typs vermeiden. Es gibt unbenennbare Typen (wie Lambdas) und fast unbenennbare Typen (wie das Ergebnis vonstd::bind
oder andere Ausdrucksvorlagen-ähnliche Dinge).Ohne
auto
müssen Sie am Ende die Daten löschen, um sie zu löschenstd::function
. Das Löschen von Typen hat Kosten.task1
Overhead beim Löschen von Typen - eine mögliche Heap-Zuordnung, Schwierigkeiten beim Inlining und Overhead beim Aufrufen virtueller Funktionstabellen.task2
hat keine. Lambdas benötigen automatische oder andere Formen des Typabzugs , um ohne Typlöschung zu speichern. andere Typen können so komplex sein, dass sie nur in der Praxis benötigt werden.Zweitens können Sie Typen falsch verstehen. In einigen Fällen funktioniert der falsche Typ scheinbar perfekt, verursacht jedoch eine Kopie.
wird kompiliert, wenn
expression()
returnBar const&
oderBar
oder sogarBar&
, woherFoo
kann konstruiert werdenBar
. Ein TemporärFoo
wird erstellt, dann gebundenf
und seine Lebensdauer verlängert, bis esf
verschwindet.Der Programmierer hat möglicherweise beabsichtigt
Bar const& f
und nicht beabsichtigt, dort eine Kopie zu erstellen, aber eine Kopie wird trotzdem erstellt.Das häufigste Beispiel ist die Art von
*std::map<A,B>::const_iterator
, wasstd::pair<A const, B> const&
nichtstd::pair<A,B> const&
der Fall ist, aber der Fehler ist eine Kategorie von Fehlern, die stillschweigend die Leistung kosten. Sie können einstd::pair<A, B>
aus einem konstruierenstd::pair<const A, B>
. (Der Schlüssel auf einer Karte ist const, da das Bearbeiten eine schlechte Idee ist.)Sowohl @Barry als auch @KerrekSB haben diese beiden Prinzipien zuerst in ihren Antworten veranschaulicht. Dies ist lediglich ein Versuch, die beiden Themen in einer Antwort hervorzuheben, wobei der Wortlaut eher auf das Problem abzielt als auf Beispiele.
quelle
Die vorhandenen drei Antworten geben Beispiele, bei denen die Verwendung von
auto
Hilfen "die Wahrscheinlichkeit einer unbeabsichtigten Pessimierung verringert" und die Leistung "verbessert".Die Münze hat eine Kehrseite. Die Verwendung
auto
mit Objekten mit Operatoren, die das Basisobjekt nicht zurückgeben, kann zu falschem (noch kompilierbarem und ausführbarem) Code führen. In dieser Frage wird beispielsweise gefragt , wie die Verwendung derauto
Eigenbibliothek zu unterschiedlichen (falschen) Ergebnissen geführt hat, dh in den folgenden Zeilenführte zu unterschiedlichen Ausgaben. Dies ist zwar hauptsächlich auf die verzögerte Auswertung durch Eigens zurückzuführen, aber dieser Code ist / sollte für den (Bibliotheks-) Benutzer transparent sein.
Während die Leistung hier nicht stark beeinträchtigt wurde, kann die Verwendung
auto
zur Vermeidung einer unbeabsichtigten Pessimierung als vorzeitige Optimierung oder zumindest als falsch eingestuft werden;).quelle