Speicherverwaltung in speicherintensiver Anwendung

7

Wenn Sie eine speicherintensive Anwendung in C ++ unter Windows entwickeln, können Sie Ihren eigenen benutzerdefinierten Speichermanager schreiben, um Speicher aus virtuellem Adressraum zuzuweisen, oder ermöglicht Ihnen CRT die Kontrolle und die Speicherverwaltung für Sie? Ich bin besonders besorgt über die Fragmentierung, die durch die Zuweisung und Freigabe kleiner Objekte auf dem Heap verursacht wird. Aus diesem Grund denke ich, dass der Speicher nicht mehr ausreicht, obwohl genügend Speicher vorhanden ist, aber fragmentiert ist.

    
Naveen 23.01.2009, 18:29
quelle

9 Antworten

38

Ich denke, Ihre beste Lösung ist es, keine zu implementieren, bis Profile beweisen , dass die CRT Speicher so fragmentiert, dass die Leistung Ihrer Anwendung beeinträchtigt wird. CRT-, Core-OS- und STL-Leute verbringen viel Zeit damit, über Speicherverwaltung nachzudenken.

Es besteht eine gute Chance, dass Ihr Code unter den vorhandenen Zuordnern sehr gut funktioniert, ohne dass Änderungen erforderlich sind. Das ist sicherlich eine bessere Chance, als wenn Sie beim ersten Mal gleich einen Speicherzuordner bekommen. Ich habe schon vorher Speicherzuweiser für ähnliche Umstände geschrieben, und es ist eine monströse Aufgabe, die ich übernehmen muss. Nicht so überraschend, die Version, die ich geerbt hatte, war voller Fragmentierungsprobleme.

Der andere Vorteil des Wartens, bis ein Profil zeigt, dass es ein Problem ist, ist, dass Sie auch wissen, ob Sie tatsächlich etwas behoben haben. Das ist der wichtigste Teil eines Performance-Fixes.

Solange Sie Standard-Auflistungsklassen und einen Algorithmus (wie STL / BOOST) verwenden, sollte es nicht sehr schwierig sein, später im Zyklus einen neuen Zuordner zu installieren, um die Teile Ihrer Codebasis zu reparieren, die dies tun muss repariert werden. Es ist sehr unwahrscheinlich, dass Sie einen handcodierten Zuordner für Ihr gesamtes Programm benötigen.

    
JaredPar 23.01.2009, 18:34
quelle
4

Obwohl die meisten von Ihnen angeben, dass Sie keinen eigenen Speichermanager schreiben sollten, könnte es immer noch nützlich sein, wenn:

  • Sie haben eine bestimmte Anforderung oder Situation, in der Sie sicher sind, dass Sie eine schnellere Version
  • schreiben können
  • Sie möchten Ihre eigene Speicherüberschreiblogik schreiben (um beim Debuggen zu helfen)
  • Sie möchten die Orte im Auge behalten, an denen Speicher verloren geht

Wenn Sie Ihren eigenen Speichermanager schreiben möchten, müssen Sie ihn in die folgenden 4 Teile aufteilen:

  1. ein Teil, der die Aufrufe von malloc / free (C) und new / delete (C ++) abfängt. Dies ist ziemlich einfach für neue / löschen (nur globale neue und lösche Operatoren), aber auch für malloc / free ist dies möglich ('überschreiben' die Funktionen des CRT, neu definieren Calls zu malloc / free, ...)
  2. ein Teil, der den Einstiegspunkt Ihres Speichermanagers darstellt und vom Interceptor-Teil
  3. aufgerufen wird
  4. ein Teil, der den eigentlichen Speichermanager implementiert. Möglicherweise haben Sie mehrere Implementierungen davon (je nach Situation)
  5. ein Teil, der den zugewiesenen Speicher mit Informationen des Aufruf-Stacks, Überschreibzonen (aka rote Zonen), ...
  6. "verziert"

Wenn diese vier Teile klar getrennt sind, wird es auch leicht, ein Teil durch ein anderes zu ersetzen oder ein neues Teil hinzuzufügen, z.B.:

  • fügen Sie die Memory Manager-Implementierung der Intel Tread Building Blocks-Bibliothek hinzu (zu Teil 3)
  • Modifizieren Sie Teil 1, um eine neue Version des Compilers, eine neue Plattform oder einen völlig neuen Compiler
  • zu unterstützen

Nachdem ich selbst einen Speichermanager geschrieben habe, kann ich nur darauf hinweisen, dass es sehr praktisch ist, einen eigenen Speichermanager einfach zu erweitern. Z.B. Was ich regelmäßig tun muss, ist Speicherlecks in lang laufenden Serveranwendungen zu finden. Mit meinem eigenen Speichermanager mache ich das so:

  • Starten Sie die Anwendung und lassen Sie sie eine Weile aufwärmen
  • Bitten Sie Ihren eigenen Speichermanager, einen Überblick über den belegten Speicher zu erstellen, einschließlich der Anruflisten im Moment des Anrufs
  • fahre mit der Anwendung fort
  • mache einen zweiten Dump
  • Sortieren Sie die beiden Dumps alphabetisch nach Aufrufliste
  • Suche nach den Unterschieden

Obwohl Sie ähnliche Funktionen mit vorgefertigten Komponenten ausführen können, haben sie einige Nachteile:

  • oft verlangsamen sie ernsthaft die Anwendung
  • oft können sie nur Lecks am Ende der Anwendung melden, nicht während die Anwendung läuft

Aber versuchen Sie auch, realistisch zu sein: Wenn Sie kein Problem mit Speicherfragmentierung, Performance, Speicherlecks oder Speicherüberschreibungen haben, gibt es keinen wirklichen Grund, einen eigenen Speichermanager zu schreiben.

    
Patrick 30.01.2010 16:26
quelle
2

War es SmartHeap von MicroQuill?

    
codekaizen 23.01.2009 18:42
quelle
2

Es gab früher eine ausgezeichnete Drop-In-Heap-Ersetzungsbibliothek von Drittanbietern für VC ++, aber ich erinnere mich nicht mehr an den Namen. Unsere App wurde um 30% schneller, als wir damit angefangen haben.

Bearbeiten: es ist SmartHeap - danke, ChrisW

    
Arkadiy 23.01.2009 18:34
quelle
2

Aus meiner Erfahrung ist die Fragmentierung meistens ein Problem, wenn Sie fortlaufend große Puffer (wie über 16k) zuweisen und freigeben, da diese diejenigen sind, die letzten Endes zu wenig Speicher zur Verfügung stellen kann keinen großen Platz für einen von ihnen finden.

In diesem Fall sollten nur diese Objekte eine spezielle Speicherverwaltung haben, den Rest einfach halten. Sie können die Wiederverwendung von Puffern verwenden, wenn sie immer die gleiche Größe haben, oder komplexeres Speicherpooling, wenn sie in der Größe variieren.

Die Standard-Heap-Implementierungen sollten kein Problem haben, einen Platz für kleinere Puffer zwischen vorherigen Zuordnungen zu finden.

    
jturcotte 23.01.2009 19:54
quelle
1
  

Sie entscheiden sich, Ihren eigenen benutzerdefinierten Speichermanager zu schreiben, um Speicher aus virtuellem Adressraum zuzuweisen oder erlauben Sie CRT, die Kontrolle zu übernehmen und die Speicherverwaltung für Sie vorzunehmen?

Die Standardbibliothek ist oft gut genug. Wenn dies nicht der Fall ist, besteht ein kleinerer Schritt darin, operator new und operator delete für bestimmte Klassen und nicht für alle Klassen zu überschreiben, anstatt sie zu ersetzen.

    
ChrisW 23.01.2009 18:45
quelle
1

Es hängt sehr stark von Ihren Speicherzuordnungsmustern ab. Aus meiner persönlichen Erfahrung heraus gibt es in der Regel ein oder zwei Klassen in einem Projekt, die besondere Aspekte bei der Speicherverwaltung erfordern, da sie häufig in dem Teil des Codes verwendet werden, in dem Sie viel Zeit verbringen. Es kann auch Klassen geben, die in einem bestimmten Kontext eine spezielle Behandlung benötigen, aber in anderen Kontexten verwendet werden können, ohne sich darum zu kümmern.

Ich verwalte oft diese Art von Objekten in einem std :: vector oder etwas Ähnliches und Explizites, anstatt die Zuordnungsroutinen für die Klasse zu überschreiben. In vielen Situationen ist der Heap wirklich übertrieben und die Zuweisungsmuster sind so vorhersagbar, dass Sie nicht auf dem Heap reservieren müssen, sondern in einer viel einfacheren Struktur, die größere Seiten aus dem Heap reserviert, die weniger Verwaltungsaufwand haben als jede einzelne Instanz der Haufen.

Dies sind einige allgemeine Dinge, über die Sie nachdenken sollten:

Zuerst sollten kleine Objekte, die schnell zugewiesen und zerstört wurden, auf den Stapel gelegt werden. Die schnellste Zuordnung sind diejenigen, die nie gemacht werden. Die Stapelzuordnung erfolgt ebenfalls ohne Sperren eines globalen Heapspeichers, was gut für Multi-Thread-Code ist. Die Zuweisung auf dem Heap in c / c ++ kann im Vergleich zu GC-Sprachen wie Java relativ teuer sein, also versuchen Sie es zu vermeiden, es sei denn, Sie benötigen es.

Wenn Sie viel zuweisen, sollten Sie auf die Threading-Performance achten. Ein klassischer Fallstrick sind String-Klassen, die dazu neigen, dem Benutzer viele Zuweisungen zu verbergen. Wenn Sie viele Threads in mehreren Threads verarbeiten, werden sie möglicherweise mit einem Mutex im Heap-Code kämpfen. Zu diesem Zweck kann die Kontrolle über die Speicherverwaltung die Dinge erheblich beschleunigen. Der Wechsel zu einer anderen Heap-Implementierung ist hier im Allgemeinen nicht die Lösung, da der Heap weiterhin global ist und Ihre Threads dagegen ankämpfen werden. Ich denke, Google hat einen Heap, der allerdings in Multithread-Umgebungen schneller sein sollte. Habe es nicht selbst ausprobiert.

    
Laserallan 23.01.2009 19:13
quelle
0

Nein, würde ich nicht.

Die Chancen, dass ich einen besseren Code schreibe als der CRT mit dem, der weiß, wie viele hundert Mannjahre er investiert hat, sind gering.

Ich würde nach einer spezialisierten Bibliothek suchen, anstatt das Rad neu zu erfinden.

    
Raz 23.01.2009 18:36
quelle
0

Es gibt eine Lösung, die von einigen Open-Source-Software wie doxygen verwendet wird, die Idee ist, einige Instanzen in Datei zu speichern, wenn Sie eine bestimmte Menge an Speicher überschreiten. Und nachdem Sie Ihre Daten erhalten haben, wenn Sie sie brauchen.

    
mike 26.01.2011 15:13
quelle