Korrekte Vektorspeicherverwaltung

8

Ich mache ein Spiel und ich habe einen Kugelvektor, der herumfliegt. Wenn das Geschoss fertig ist, mache ich bullets.sease (bullets.begin () + i); Dann verschwindet die Kugel. Es scheint jedoch nicht, um die Erinnerung zu bekommen. Wenn ich 5000 Kugeln erzeuge, dann 5.000 mehr, wenn diese sterben, bleibt der Speicher gleich, aber wenn ich 5000 mehr kreiere, während diese 5000 fliegen, wird es neuen Raum zuweisen. Was muss ich tun, um diesen Speicher tatsächlich freizugeben?

Danke

    
jmasterx 12.02.2010, 18:03
quelle

6 Antworten

15

Die Klasse std::vector verwaltet automatisch ihren internen Speicher. Es wird so erweitert, dass es so viele Objekte enthält, wie Sie hineinlegen, aber im Allgemeinen wird es nicht von alleine schrumpfen, wenn Sie Objekte entfernen (obwohl es natürlich den Speicher freigeben wird, wenn es zerstört wird).

Das std::vector hat zwei relevante Konzepte von "Größe". Zuerst ist die "reservierte" Größe, dh wieviel Speicher sie vom System für die Speicherung von Vektorelementen zugewiesen hat. Die zweite ist die Größe "used", also wie viele Elemente sich logisch im Vektor befinden. Natürlich muss die reservierte Größe mindestens so groß wie die verwendete Größe sein. Sie können die verwendete Größe mit der size() -Methode (die Sie sicher schon kennen) ermitteln und die reservierte Größe mit der capacity() -Methode ermitteln.

Wenn die verwendeten und reservierten Größen identisch sind und Sie versuchen, ein neues Element einzufügen, weist der Vektor normalerweise einen neuen internen Puffer mit der doppelten reservierten Größe zu und kopiert alle vorhandenen Elemente in diesen Puffer. Dies ist für Sie transparent, außer dass es alle Iteratoren ungültig macht, die Sie halten. Wie bereits erwähnt, AFAIK, werden die meisten STL-Implementierungen die reservierte Größe als Antwort auf eine Löschung nie verkleinern.

Leider können Sie einen Vektor dazu zwingen, seine reservierte Größe mit der Methode reserve() zu erhöhen, dies funktioniert jedoch nicht, wenn die reservierte Kapazität verringert. Soweit ich das beurteilen kann, ist es das Beste, eine Kapazitätsreduzierung zu erreichen, indem Sie Folgendes tun:

%Vor%

Damit wird ein temporärer Vektor erzeugt, der eine Kopie des ursprünglichen Vektors ist (aber mit der minimal erforderlichen Kapazität) und dann die internen Puffer der beiden Vektoren vertauscht. Dies führt dazu, dass der ursprüngliche Vektor dieselben Daten, aber möglicherweise eine kleinere reservierte Größe aufweist.

Nun, da das Erstellen dieser temporären Kopie eine relativ teure Operation ist (d. h. es benötigt viel mehr Prozessorzeit als normales Lesen / Einfügen / Löschen), möchten Sie es nicht jedes Mal tun, wenn Sie ein Element löschen. Aus demselben Grund verdoppelt der Vektor seine reservierte Größe, anstatt sie um 1 zu erhöhen, wenn Sie die vorhandene Größe überschreiten müssen. Daher würde ich empfehlen, dass, nachdem Sie eine relativ große Anzahl von Elementen gelöscht haben und Sie wissen, dass Sie nicht so viele weitere hinzufügen werden, führen Sie den Swap-Trick oben aus, um die Kapazität zu reduzieren.

Schließlich möchten Sie vielleicht auch etwas anderes als std::vector dafür verwenden. Das Löschen von Elementen aus der Mitte eines Vektors, die anscheinend häufig ausgeführt werden, ist im Vergleich zu vielen anderen Arten von Datenstrukturen eine langsame Operation (da der Vektor alle nachfolgenden Elemente um einen Platz zurückkopieren muss, um das Loch zu füllen) . Welche Datenstruktur für Ihre Zwecke am besten geeignet ist, hängt davon ab, was Sie sonst noch mit den Daten tun.

    
Tyler McHenry 12.02.2010, 18:13
quelle
4

Zunächst ist die std :: vector Erase-Methode nicht sehr effektiv, sie muss alle Elemente nach der gelöschten verschieben. Wenn die Reihenfolge der Vektorelemente (Aufzählungszeichen) keine Rolle spielt, wird das gelöschte Aufzählungszeichen mit dem letzten Aufzählungszeichen ausgetauscht und das letzte Aufzählungszeichen wird schneller gelöscht (Sie erhalten also eine konstante Komplexität anstatt einer linearen Komplexität).

Zweitens, was ist das eigentliche Problem - dass nach dem Löschen der 10.000 Elemente der Speicher nicht frei wird? Sprechen wir über vom Betriebssystem gemeldeten freien Speicher oder freien Speicherplatz auf dem Heap? Es ist möglich (und sehr wahrscheinlich), dass ein anderes Objekt nach der Position der Daten des Vektors zugewiesen wurde, so dass es nicht möglich ist, diesen Speicher einfach dem Betriebssystem zu überlassen; aber es kann für andere, neu erstellte Objekte wiederverwendet werden.

    
Messa 12.02.2010 18:12
quelle
3

Ich schlage vor, dass Sie sich diese zwei Idiome ansehen und das auswählen, das Ihnen am besten passt:
Shrink to fit
Clear & amp; minimieren

    
the_drow 12.02.2010 18:13
quelle
3

Normalerweise verhält sich das Speicherzuordnungsmodell des Vektors so, dass es eine amortisierte konstante Zeit liefert push_back Operation. Im Grunde versucht es zu erraten, dass Sie den gelöschten Teil mit einem neuen Element füllen wollen, damit der Speicher nicht freigegeben wird. Auf diese Weise kann eine ständige Zuordnung und Deallokation vermieden werden. Um dies zu umgehen, können Sie den Auslagerungstrick verwenden, um den ungenutzten Vektorspeicher freizugeben. Sie müssen Ihren leeren Vektor mit einem temporären unbenannten Vektor austauschen, so dass, wenn der temporäre Vektor den Gültigkeitsbereich verlässt, der Speicher in seinem Destruktor freigegeben wird, etwa so: vector<int>(c).swap(c)

    
Naveen 12.02.2010 18:09
quelle
2

Es kann nicht die Erinnerung loswerden.
Aber wenn Sie das nächste Mal einen Bullit hinzufügen müssen, müssen Sie nicht mehr Platz reservieren.
Es wird den Speicher, aus dem das gelöschte Geschoss stammt, nicht wiederverwenden.

Hinweis:
Wenn Sie relativ häufig aus der Mitte eines Containers löschen, ist der Vektor möglicherweise nicht der richtige Container. Dies liegt daran, dass Sie alle Elemente von [n + 1, Ende) um ein Leerzeichen im Speicher nach unten verschieben müssen, wenn Sie das Element n entfernen.

    
Martin York 12.02.2010 18:13
quelle
0
  

Wenn das Geschoss fertig ist, mache ich bullets.sease (bullets.begin () + i);

Tu das nicht. Wenn mehrere Aufzählungszeichen pro Bild abgeschlossen sind, erhalten Sie eine schreckliche Leistung, da die Aufzählungszeichen, die nicht fertig sind, immer wieder kopiert werden, was wirklich nicht notwendig ist. Hier ist, was ich tun würde:

%Vor%

Bei diesem Ansatz wird jedes Live-Geschoss höchstens einmal bewegt.

    
fredoverflow 02.03.2010 15:19
quelle

Tags und Links