2009-08-20 15 views
23

Mein Arbeitskollege behauptet, dass für Objekttypen Vorinkrement effizienter ist als Post-InkrementLeistungsunterschied zwischen ++ Iterator und Iterator ++?

z.B.

std::vector<std::string> vec; 

... insert a whole bunch of strings into vec ... 

// iterate over and do stuff with vec. Is this more efficient than the next 
// loop? 
std::vector<std::string>::iterator it; 
for (it = vec.begin(); it != vec.end(); ++it){ 

} 

// iterate over and do stuff with vec. Is this less efficient than the previous loop? 
std::vector<std::string>::iterator it; 
for (it = vec.begin(); it != vec.end(); it++){ 

} 
+1

Ziemlich nah an ein Duplikat von http://stackoverflow.com/questions/24901/is-there-a-performance-difference-between-i-and-i-in-c. Diese Frage spezifiziert Iteratoren, während die verknüpfte Frage allgemeiner ist. – Eclipse

+0

Danke, dass Link eine gute Antwort hat. – Matt

Antwort

48

Postinkrement muss den Wert zurückgeben der Iterator hatte, bevor es Inkrementieren war; Der vorherige Wert muss also irgendwo kopiert werden, bevor er mit dem richtigen Inkrement geändert wird. Die zusätzliche Arbeit kann ein wenig oder viel sein, aber sie kann sicherlich nicht weniger als Null sein, verglichen mit einer Vorinkrementierung, die einfach die Inkrementierung durchführen und dann den gerade geänderten Wert zurückgeben kann - kein Kopieren // Speichern // usw. notwendig.

Also, es sei denn, Sie müssen POSITINCREMEN ausdrücklich haben (weil Sie den "Wert vor Inkrement" in irgendeiner Weise verwenden), sollten Sie stattdessen immer preincrement verwenden.

+0

Es gibt wirklich keinen Grund dafür, dass die Standard-Iteratoren langsamer sind; ist der Compiler nicht erlaubt, Sachen in std :: anzunehmen, verhält sich wie der Standard spezifiziert, und macht so die selbe Optimierung, die er für eingebaute Typen tut? – derobert

+2

derobert: Ja, wenn es geht. Nicht alle Iteratortypen sind einfach genug, um diese Art der Optimierung durchzuführen. Wenn das Objekt groß genug ist, kann es zu einer Verlangsamung kommen. Es wird wohl fast nie etwas ausmachen, aber man benutzt dieses Idiom in engen Schleifen, wo es wirklich möglich ist. – quark

+0

Art einer populären Folklore-esque Frage. Schön, ein paar echte Details zu hören. –

5

Für primitive Typen, habe ich dies zu tun. Es hat mir 1998/1999 in vielen Programmen von Visual Studio eine Steigerung von 10% gebracht. Kurz darauf haben sie ihren Optimierer repariert. Der Post-Inkrement-Operator hat eine Variable auf dem Stapel erstellt, den Wert kopiert, die Quelle inkrementiert und anschließend die Variable aus dem Stapel entfernt. Sobald die Optimierer erkannt hatten, dass sie nicht verwendet wurde, ging der Nutzen verloren.

Die meisten Leute hörten nicht auf diese Weise zu codieren. :) Es hat ein paar Jahre gedauert.

Für Objekte bin ich mir nicht sicher, wie das funktionieren würde. Ich nehme an, dass ein Kopieroperator für eine echte Implementierung von Post-Inkrementen erforderlich wäre. Es müsste eine Kopie erstellen und die Kopie für die Operationen verwenden und die Quelle erhöhen.

Jacob

16

Die Standardinkrement Betreiber wie folgt aussehen:

class X 
{ 
    X operator++(int) // Post increment 
    { 
     X tmp(*this); 
     this->operator++(); // Call pre-increment on this. 
     return tmp; 
    } 
    X& operator++() // Pre increment 
    { 
     // Do operation for increment. 
     return *this; 
    } 
}; 

Beachten Sie die Post-Inkrement wird in Bezug auf die Pre-Inkrement definiert. Post-increment macht also die gleiche Arbeit plus etwas extra. Normalerweise erstellt dies eine Kopie und gibt dann die Kopie über eine Kopie zurück (das Pre-Inkrement kann sich selbst als Referenz zurückgeben, aber das Post-Inkrement kann nicht).

+1

Sie haben zwei Postfix-Einsen dort. –

+0

Wie meinst du zwei Postfixes? Ich kann Post und Pre hier sehen. Ich habe es sogar getestet. – flyjohny

+0

@flyjohny: Der Kommentar ist ein Jahr alt und bezog sich auf die erste Version des Beitrags, bevor ich es korrigierte: http://Stackoverflow.com/posts/1304020/ Revisionen –

3

Im Ernst, es sei denn, Sie tatsächlich zu rufen Nachinkrement statt Prä-Inkrement der Leistungsunterschied WILL NUR MATTER IN SEHR seltenen Fällen Leistungsprobleme aufgrund haben.


aktualisiert

Zum Beispiel, wenn Ihre Software ist eine Klimasimulation und operator++ bewirkt, dass die Simulation einen Schritt vorwärts bewegen (dh ++simulation gibt Ihnen den nächsten Schritt in der Simulation, während simulation++ erstellt eine Kopie des aktuellen Schritts in der Simulation, führt die Simulation fort und übergibt Ihnen dann die Kopie), dann wird die Leistung ein Problem sein.

Realistischer, in einem Kommentar erwähnt der ursprüngliche Fragesteller, dass wir ein eingebettetes Projekt mit (scheinbar) Echtzeitanforderungen diskutieren. In diesem Fall müssen Sie sich Ihre spezifische Implementierung ansehen.Ich bezweifle ernsthaft, dass Sie merkliche Verlangsamungen haben werden, die durch eine std::vector mit Post-Inkrement anstelle von Pre-Inkrement durchlaufen, obwohl ich keine STL-Implementierung in dieser Angelegenheit Benchmarking.


Wählen Sie aus Leistungsgründen nicht den anderen. Wählen Sie eines aus Gründen der Klarheit/Wartung aus. Persönlich setze ich das Vorinkrementieren fort, weil es mir im Allgemeinen egal ist, was der Wert vor dem Inkrementieren war.

Wenn Ihr Gehirn explodiert, wenn Sie ++i statt i++ sehen, kann es Zeit sein, in eine andere Arbeitslinie zu gehen.

+2

Richtig . Aber Sie sollten Pre-Inkrement bevorzugen. Der Grund dafür ist, dass, wenn jemand einen zugrunde liegenden Typ im System ändert, Sie nicht zurückgehen müssen, um zu überprüfen, ob alle Post-Increments effizient sind oder nicht. Aus Gründen der Wartungsfreundlichkeit sollten Sie daher eine Vorinkrementierung bevorzugen. –

+0

Ich habe noch keine Leistungsprobleme, aber es zahlt sich aus, an die Leistung in den Systemen zu denken, an denen ich gerade arbeite. Die Software arbeitet im Telcom-Bereich und bearbeitet Tausende von Anrufen pro Sekunde. Ich dachte, ich würde einfach fragen, ob es einen Leistungsunterschied gibt und offensichtlich gibt es. – Matt

+0

Sehr korrekt.Aber die Frage wollte wissen, welche nur effizienter ist. – n002213f

0

Ich weiß, das ist wie "Hangar-Flying" in der Luftfahrt. Ich bin sicher, dass es rein akademisch ist, und Sie wissen, wenn es einen Unterschied macht, müssen Sie Ihren Code neu denken.

Beispiel: Ich habe einen Kommentar auf einige Antwort, die ich bis zu einem gewissen Frage zu Performance-Tuning gab, und es ging so:

Person: Ich habe versucht, Ihre Methode zufällig stoppen und an den Call-Stack suchen. Ich habe es mehrmals gemacht, aber es macht keinen Sinn. Die ganze Zeit ist es tief in irgendeinem Iterator-Inkrement-Code. Was ist das gut?

Ich: Das ist großartig! Es bedeutet, dass fast die gesamte Zeit in inkrementelle Iteratoren geht. Schau einfach höher auf den Stapel und sieh, wo er herkommt. Ersetzen Sie das durch einfaches Integer-Inkrement, und Sie erhalten eine massive Beschleunigung.

Natürlich bevorzugen Sie vielleicht die Schönheit der Iterator-Inkrementierung, aber es ist einfach genug, um herauszufinden, ob es Sie viel Leistung kostet.

+0

Gibt es in Ihrer Umgebung keine brauchbaren Profiler, die es ermöglichen, die gleichen Informationen auf eine stabilere Art und Weise zu bekommen, als den Stapel willkürlich anzuhalten? Sampling-Profiler machen im Wesentlichen dasselbe, nur viel öfter und automatisch. – Blaisorblade

+0

@Blaisorblade: Schauen Sie sich die PDF-Diashow * [hier] an (http://sourceforge.net/projects/randompausedemo/files/) *. Profiler basieren auf niedrigen Erwartungen. –

+0

Ich muss es ausprobieren, aber deine Idee macht Sinn. – Blaisorblade

4

Ich habe gelesen, dass es keinen Unterschied mit Compilern macht, die einen anständigen Optimierer haben. Da die Gesamtschleifenlogik für beide gleich ist, ist es sinnvoll, ein Vorinkrement als eine gute Programmiergewohnheit zu verwenden. Wenn der Programmierer den "suboptimalen" Compiler findet, ist das beste Ergebnis garantiert.