2009-03-06 5 views
5

Ich habe eine Frage zum std :: vector.Reinigen Sie jede Schleifeniteration mit einem Vektor. Was ist der speichereffizienteste Weg?

Ich habe einen sehr speicherintensiven Algorithmus, bei dem ich voraussage, dass die Vorhersage von Vektorgrößen und die Reservierung von genügend Speicher für die Vektoren im Voraus mir sehr dabei helfen werden, den Speicherverbrauch zu reduzieren.

Welche der folgenden ist besser:

for (...) { 
    std::vector<Type> my_vector; 
    my_vector.reserve(stuff_count); 
    // Do stuff , and append stuff to my_vector. 
} 

Oder diese:

std::vector my_vector; 
for (...) { 
    my_vector.clear(); 
    my_vector.reserve(stuff_count); 
    // Do stuff , and append stuff to my_vector. 
} 

Sagen Sie mir bitte, was am besten ist, oder wenn es eine noch bessere Möglichkeit Sachen zu tun.

Vielen Dank im Voraus!

+0

Dies ist keine Frage zum Meinungstyp. Sie sollten den Unterschied für Ihr spezielles System messen. –

Antwort

11

Mit der ersten Variante weisen Sie den Puffer des Vektors bei jeder Iteration neu zu - das ist normalerweise ziemlich teuer. Bei der zweiten Variante werden Sie nur gelegentlich neu zugeordnet. Die zweite Variante ist besser, da Geschwindigkeit für Sie Priorität hat.

Es ist unklar von dir Frage, woher die Anzahl der Elemente bekannt ist. Vielleicht können Sie sogar schnell die maximale Anzahl an Elementen für alle Iterationen berechnen, diese als Puffergröße festlegen und keine Neuzuweisung haben.

+0

das ist normalerweise ziemlich teuer: Das ist eine mutige Aussage ohne Profiling. Der C++ - Speicherzuordner wurde für genau diese Art von Situation entwickelt, so dass ich daran zweifle (aber selbst diese Behauptung ist fett und muss getestet werden). –

+0

Nun, wir mussten uns einmal in einer ähnlichen Situation profilieren. Solche Umteilungen im Extremfall waren für etwa 95% der Zeit verantwortlich - ein Puffer wurde zugewiesen, Daten wurden kopiert, kurz analysiert und dann wurde der Puffer verworfen. Das war ziemlich überraschend. – sharptooth

+0

Dies führt zu der Schlussfolgerung, dass, wenn man schnelle Ausführung wünscht, er unnötige Neuzuweisungen vermeiden sollte. – sharptooth

5

Die zweite könnte etwas schneller sein, aber ich finde die erste sauberer.

+0

Sauber ist in Ordnung, aber in meiner Situation ist sauber egal, da mein Algorithmus so viel Optimierung wie möglich benötigt. Meine nicht optimierten Tests konnten 10 GB Speicher problemlos nutzen. – Nailer

+0

Dann sollten Sie keine Fragen wie diese stellen (die Meinung eines jeden kann sehr/falsch sein). Was Sie tun sollten, ist, den Code zu profilieren und ihn auf einem kleineren Datensatz zu speichern. –

5

Da die Unterschiede im Code trivial sind, testen Sie beide Ansätze und sehen Sie, welche für Ihre spezielle Anwendung am besten geeignet sind.

1

Es hängt ein wenig davon ab, wie Type gebaut/zerstört werden muss, würde ich sagen. Wenn es ein POD ist, die nicht Zerstörung erfordert, können Sie die klare überspringen(), die die Destruktoren alle in einer Schleife aufruft, und es als statisches Array verwenden, anstatt:

std::vector<Type> my_vector(size); 
for (...) 
{ 
    int index = 0; 
    // Do stuff 
    my_vector[index] = some_value; 
} 

(Caveat: Code ungetestet)

+0

Wenn es ein POD ist, würde ich erwarten, dass der Vektor die Zerstörung auf jeden Fall optimiert. Testen Sie dies, bevor Sie voreilige Schlüsse über die Leistung ziehen. – jalf

+0

Einverstanden. (mit Jalf) –

+0

In der Theorie haben Sie Recht, jalf. Nehmen Sie meine Antwort als einen Hinweis, dass Sie die Dinge oft zerstören könnten, und dass dort wahrscheinlich etwas Arbeit geleistet werden kann. –

1

... genug Speicher für die Vektoren im Voraus reserviert werden mir helfen, eine Menge mit der Verringerung der Speichernutzung

err ... was ?! Das ergibt überhaupt keinen Sinn. Das Reservieren von Speicher hilft nicht dabei, die Speichernutzung in irgendeiner Weise zu reduzieren. Es verhindert die Notwendigkeit einer ständigen Neuzuweisung, die Dinge schneller macht, aber bis Verwendung geht, erhalten Sie keinen Vorteil.

+0

In der Tat wird die Anzahl der freien Bytes gleich sein, aber der größte zuweisbare Chunk wird kleiner sein: das erneute Zuweisen immer und immer wieder, bis Sie die richtige Größe haben, wird schließlich Ihren Heap fragmentieren. Schlussendlich. – xtofl

+0

Darüber hinaus müssen Vektoren einen fortlaufenden Speicher haben. Wenn also der aktuelle Vektor nicht mehr erweitert werden kann, muss ein ganz neuer Block zugewiesen werden, der alte Vektor kopiert und freigegeben werden. Vorübergehend (oldsize + newsize) Speicher verwenden. Wenn Sie große Speicheranforderungen haben, wird dies relevant. – Pieter

+0

Vektoren ordnen das doppelte des Speichers zu, den sie benötigen, wenn Sie die Append-Funktion aufrufen und nicht genügend Platz für das nächste Element vorhanden ist. Das heißt, wenn ich einen Vektor gerade groß genug für 100 Elemente habe, wird der Vektor, wenn ich das 101. Element hinzufüge, genug Speicher für 200 Elemente zuweisen. – Nailer

6

Ich gehe davon aus, dass die Vorhersage von Vektorgrößen und die Reservierung von genügend Speicher für die Vektoren im Voraus mir sehr dabei helfen werden, den Speicherverbrauch zu reduzieren.

Versuchen Sie und handeln Sie wie ein Ingenieur kein Wahrsager. Erstellen Sie einen Test und messen Sie den Unterschied.

0

Wenn Sie viele Attends ausführen müssen, verwenden Sie std :: deque anstelle von std :: vector und verwenden Sie einfach "push_back".

0

Wie wäre es?

+0

Warum nicht std :: copy()? –

1

Der zweite wird den maximalen Speicher aller Verwendungen durch die Schleife verwenden, d. H. Die maximale Größe der stuff_count-Typen. std::vector::clear() does not necessarily free memory. Das heißt, wenn Sie std::vector::capacity() vor und nach std::vector::clear() aufrufen, könnte eine standardkonforme Implementierung denselben Wert zurückgeben.

Im besten Fall reduzieren Sie die Anzahl der Speicherzuweisungen mit dem obigen Schema. Aber Sie werden den Speicherbedarf an keiner Stelle reduzieren. Wenn Sie auf die reservierte Speichermenge verkleinern mögen, sollten Sie das Vektor-Swap-Idiom verwenden:

std::vector<type>().swap(my_vector); 
my_vector.reserve(stuff_count); 

oder die erste Lösung, da die Gesamtwirkung wird das gleiche sein.