Index oder Iterator?
-
Siehe dazu auch eine von mir angestoßene Diskussion: http://www.c-plusplus.net/forum/283008
Edit: Ok, hat nur peripher damit zu tun, aber egal... Assoziationen schaffen ist doch immer gut.
-
EOutOfResources schrieb:
Normalerweise sollte ein Indexoperator einem Iterator (was die Geschwindigkeit angeht) überlegen sein.
Theoretisch ist das falsch, da die Iteratoren eine Addition eines Offsets pro Schleifendurchlauf weniger machen müssen.
Praktisch hängt es aber von den Fähigkeiten des verwendeten Compilers ab.Habe gerade den Visual-C++-Express-2008 zur Hand - da sieht das so aus: folgender Code
vector< int > v(10000,2); for( int n=1200; --n; ) // damit ich überhaupt eine Zeit messen kann { for( int i=0; i<v.size(); ++i ) ++v[i]; }braucht auf meine PC ca. 15ms
und mit Iterator
for( int n=1200; --n; ) { for( vector< int >::iterator i = v.begin(); i != v.end(); ++i ) ++*i; }schafft es nur in 84ms - also deutlich langsamer. Sieht man sich den Maschinencode an, so ist der deutlich suboptimal.
Aber der Sieger ist:
struct inc { void operator()( int& i ) const { ++i; } }; // ... for( int n=1200; --n; ) { for_each( v.begin(), v.end(), inc() ); }mit nur 8ms, also fast doppelt so schnell wie die Index-Variante. Wer hätte das gedacht!
Gruß
Werner
-
Werner Salomon schrieb:
mit nur 8ms, also fast doppelt so schnell wie die Index-Variante. Wer hätte das gedacht!
Hmm. Das dürfte aber lediglich von dem
size()herrühren.for_eachmacht ansonsten genau das gleiche, wie die erste Variante.Hast du das auch mit dem extrahiertem size Aufruf probiert? Sollte dann das gleiche Ergebnis geben.
-
Werner Salomon schrieb:
EOutOfResources schrieb:
Normalerweise sollte ein Indexoperator einem Iterator (was die Geschwindigkeit angeht) überlegen sein.
Theoretisch ist das falsch, da die Iteratoren eine Addition eines Offsets pro Schleifendurchlauf weniger machen müssen.
Nee, es ist theoretisch oft wahr, weil der Prozessor den Indexzugriff kostenlos schafft, und bei zwei beteiligten Arrays gleich ein Register weniger gebraucht wird, und das ist frei für weiter außen liegende Aufgaben. Oder überhaupt erst verwendbar. (Frei aus dem AMD Optimierungshandbuch, die sprachen von register pressure.)
Praktisch sollte hier kein Unterschied messbar sein. Falls doch, sind es die zusätzlichen Debug-Funktionen des MSVC, die schaltet man AFAIR nicht aus, wenn man auf Release umstellt, sondern muß noch komische Makros dazu setzen. Oder war das erst ab dem Visual Studio 2010?
hustbaers "Richtige Antwort: kann so oder so ausgehen." kann ich nur zustimmen, möchte es noch ergänzen mit "Und zwar aus Gründen, die außerhalb des zu messenden Codes liegen."
-
Das hier
for( int i=0; i!=v.size(); ++i ) ++v[i];ergibt bei mir mit
g++ -march=native -DNDEBUG --save-temps -O3 main.cpp
lediglich "normalen" assembler-code.Aber
int s=v.size(); for( int i=0; i!=s; ++i ) ++v[i];erzeugt MMX-Code.
Außerdem erzeugen die Iterator-Varianten MMX-Code, egal, ob ich mir end() vorher in einer Variablen merke.
???
-
Werner Salomon schrieb:
for( int n=1200; --n; ) { for( vector< int >::iterator i = v.begin(); i != v.end(); ++i ) ++*i; }schafft es nur in 84ms - also deutlich langsamer. Sieht man sich den Maschinencode an, so ist der deutlich suboptimal.
Auch mit
#define _SECURE_SCL 0probiert?.
Man kann zwar mal drüber nachdenken und zum Spaß auch mal nachmessen aber nach Optimierungen sind die Unterschiede so gering oder überhaupt nicht vorhanden, dass ich die Entscheidung bei sowas nie aufgrund von ein paar nanosekunden treffen würde. Iteratoren haben eigentlich immer Vorteile und den Index benutze ich bei Random Access Conatinern eigentlich nur, wenn ich ihn noch irgendwie anders benutze und beim Einsatz von Iteratoren einen zusätzlichen Zähler definieren müsste.
Sofern ohne große Umstände anwendbar finden man aber das Optimum (in Effektivität, Flexibilität, Lesbarkeit (ok, ist oft auch geschmacksache)) oft in<algorithm>
-
Ich habs mal so mit
for_each()aus<algortithm>versucht:std::for_each(objects.begin(), objects.end(), [](std::unique_ptr<object> ptr) {ptr->doSomething();});Über Lesbarkeit lässt sich streiten, jedoch bekomm ich imemr folgende Fehlermeldung:
error C2248: "std::unique_ptr<_Ty>::unique_ptr": Kein Zugriff auf private Member, dessen Deklaration in der std::unique_ptr<_Ty>-Klasse erfolgte.
-
Nimm den Pointer als const-Referenz.
-
Danke, so klappts, aber wieso muss ich hier eine Referenz verwenden?
-
Weil
unique_ptrnoncopyable ist.
-
Die Fehlermeldung bezieht sich übrigens auf den copy-ctor, der eben wegen noncopyableness private ist. Vielleicht hilft Dir das?