2008-09-29 18 views
11

Ich arbeite gerade an einem Projekt für medizinische Bildverarbeitung, das viel Speicherplatz benötigt. Kann ich etwas tun, um die Heap-Fragmentierung zu vermeiden und den Zugriff auf Bilddaten zu beschleunigen, die bereits in den Speicher geladen wurden?Wie vermeidet man die Heapfragmentierung?

Die Anwendung wurde in C++ geschrieben und läuft unter Windows XP.

EDIT: Die Anwendung hat einige Vorverarbeitung mit den Bilddaten, wie Neuformatierung, Look-up-Tabellen zu berechnen, Teilbilder von Interesse zu extrahieren ... Die Anwendung über 2 GB RAM während der Verarbeitung benötigt, von denen etwa 1 Für die Bilddaten können 5 GB verwendet werden.

+0

Ich denke, Sie wollen wahrscheinlich Fragmentierung nicht Defragmentierung vermeiden? –

+0

Meine Wette ist auf C++ - aber das ist wahr: Die Frage kann nicht beantwortet werden, solange das zugrundeliegende "Paradigma" nicht bekannt ist .... – Georgi

+0

Danke, Douglas, das habe ich korrigiert! –

Antwort

14

Wenn Sie medizinische Bildverarbeitung machen, ist es wahrscheinlich, dass Sie große Blöcke auf einmal zuweisen (512x512, 2-Byte pro Pixel Bilder). Fragmentierung wird Sie beißen, wenn Sie kleinere Objekte zwischen die Zuordnungen von Bildpuffern zuweisen.

Das Schreiben eines benutzerdefinierten Zuordners ist für diesen speziellen Anwendungsfall nicht unbedingt schwierig. Sie können den standardmäßigen C++ - Zuordner für Ihr Image-Objekt verwenden, aber für den Pixelpuffer können Sie benutzerdefinierte Zuordnungen verwenden, die alle in Ihrem Image-Objekt verwaltet werden.Hier ist ein schneller und schmutziger Umriss:

  • Verwenden Sie ein statisches Array von Strukturen, jede Struktur hat:
    • Ein festen Teil des Speichers, der N Bilder aufnehmen kann - die Chunking Kontrolle Fragmentierung helfen - versuchen ein anfänglicher N von 5 oder so
    • eine parallele Anordnung von bools angibt, ob das entsprechende Bild in Gebrauch ist
  • das Array für einen leeren Puffer zu reservieren, suchen und seine Flagge gesetzt
    • Falls keine gefunden wird, eine neue Struktur an das Ende des Arrays append
  • freizugeben, die entsprechenden Puffer in dem Array (s), und Löschen des boolesches Flag

Dies ist nur eine einfache Idee mit viel Raum für Abwechslung. Der Haupttrick besteht darin, die Bildpixelpuffer nicht freizugeben und neu zuzuordnen.

+0

Hallo Jeff, hast du jemals an medizinischer Bildverarbeitung gearbeitet? –

+1

Aus und wieder :) Sie möchten vielleicht vtk oder sogar osirix für Referenzmaterial ... Eigentlich ist die obige Idee nur eine Art von Standard-Art der Handhabung von gleichmäßig großen benutzerdefinierten Zuordnung in C++ obwohl ... –

2

Ohne viel mehr Informationen über das Problem (zum Beispiel Sprache), eine Sache, die Sie tun können, ist die Zuteilung Abwanderung zu vermeiden, indem Sie Zuteilungen wiederverwenden und nicht zuteilen, operieren und befreien. Verteiler wie dlmalloc behandelt Fragmentierung besser als Win32-Heaps.

1

Erraten Sie hier, dass Sie vermeiden Fragmentierung und nicht vermeiden Defragmentierung. Außerdem raten Sie, dass Sie mit einer nicht verwalteten Sprache arbeiten (wahrscheinlich C oder C++). Ich würde vorschlagen, dass Sie große Speicherblöcke zuweisen und dann Heap-Zuweisungen aus den zugeordneten Speicherblöcken bereitstellen. Dieser Speicherpool, der große Speicherblöcke enthält, ist anfällig für Fragmentierung. Zusammenfassend sollten Sie einen benutzerdefinierten Speicherzuordner implementieren.

Siehe einige allgemeine Ideen zu diesem here.

1

Ich glaube, Sie verwenden etwas unmanaged, weil in verwalteten Plattformen das System (Garbage Collector) für Fragmentierung sorgt.

Für C/C++ können Sie einen anderen als den Standardzuordner verwenden. (Es gab schon einige Threads über Allokatoren auf Stackowerflow).

Sie können auch Ihren eigenen Datenspeicher erstellen. Zum Beispiel haben wir in dem Projekt, an dem ich gerade arbeite, einen benutzerdefinierten Speicher (Pool) für Bitmaps (wir speichern sie in einem großen zusammenhängenden Stück Speicher), weil wir viele davon haben, und wir verfolgen den Haufen Fragmentierung und Defragmentierung, wenn die Fragmentierung zu groß ist.

+0

Fragmentierung ist unabhängig von der Speicherbereinigung. Es tritt auf, wenn langlebige Objekte über den gesamten Heap verstreut sind, weil ihre Zuweisung mit denen von kurzlebigen Objekten vermischt ist. Wie das kurzlebige Zeug freigegeben wird, ist unerheblich. – dmckee

+0

Speicherlecks sind natürlich eine andere Sache. – dmckee

+2

Ein guter Garbage Collector kümmert sich um die Fragmentierung, indem Objekte verschoben und Referenzen aktualisiert werden. – Constantin

1

Möglicherweise müssen Sie die manuelle Speicherverwaltung implementieren. Sind die Bilddaten langlebig? Ist dies nicht der Fall, können Sie das vom Apache-Webserver verwendete Muster verwenden: Weisen Sie große Speichermengen zu und verpacken Sie sie in Speicherpools. Übergeben Sie diese Pools als letztes Argument in Funktionen, damit sie den Pool verwenden können, um die Zuweisung von temporärem Speicher zu erfüllen. Sobald die Aufrufkette beendet ist, sollte der gesamte Speicher im Pool nicht mehr verwendet werden, so dass Sie den Speicherbereich putzen und erneut verwenden können. Zuordnungen sind schnell, da sie nur einen Wert zu einem Zeiger hinzufügen. Die Freigabe ist sehr schnell, da Sie sehr große Speicherblöcke auf einmal freigeben.

Wenn Ihre Anwendung Multithread ist, müssen Sie den Pool möglicherweise im lokalen Thread-Speicher speichern, um Cross-Thread-Kommunikationsaufwand zu vermeiden.

5

Es gibt Antworten, aber es ist schwierig, allgemein zu sein, ohne die Details des Problems zu kennen.

Ich nehme 32-Bit Windows XP.

Versuchen Sie, 100 MB zusammenhängende Speicher zu vermeiden, wenn Sie Pech haben, werden einige zufällige DLLs an ungünstigen Punkten durch Ihren verfügbaren Adressraum laden schnell sehr große Bereiche zusammenhängenden Speicher schneiden. Abhängig davon, welche APIs Sie benötigen, kann dies ziemlich schwierig zu verhindern sein. Es kann ziemlich überraschend sein, dass es Ihnen nichts bringt, einen letzten "kleinen" 40MB-Block zu reservieren, wenn Sie nur ein paar 400MB Speicherblöcke zusätzlich zu einer "normalen" Speichernutzung zuweisen.

Auf der anderen Seite, vorberechnen vernünftige Größe Chunks auf einmal. In der Größenordnung von 10 MB oder so ist eine gute Blockgröße. Wenn Sie es schaffen, Ihre Daten in diese Art von Größenabschnitten zu partitionieren, können Sie den Adressraum einigermaßen effizient füllen.

Wenn der Adressraum immer noch knapp wird, müssen Sie in der Lage sein, Blöcke basierend auf einem Caching-Algorithmus ein- und auszublenden. Die Auswahl der richtigen Blöcke für die Auslagerung hängt sehr stark von Ihrer Verarbeitungsalgorithm ab und erfordert eine sorgfältige Analyse.

Die Entscheidung, wo Sie die Dinge veröffentlichen, ist eine andere Entscheidung. Sie könnten beschließen, sie nur in temporäre Dateien zu schreiben. Sie können auch die Address Windowing Extenstions-API von Microsoft untersuchen. In jedem Fall müssen Sie in Ihrem Anwendungsdesign vorsichtig sein, um alle Zeiger zu bereinigen, die auf etwas verweisen, das gerade ausgelagert wird, da sonst wirklich schlimme Dinge (tm) passieren.

Viel Glück!

4

Wenn Sie Operationen auf einer großen Bildmatrix ausführen möchten, sollten Sie eine Technik namens "Tiling" in Betracht ziehen. Die Idee ist allgemein, das Bild in den Speicher zu laden, so dass derselbe zusammenhängende Block von Bytes keine Pixel in einer Zeile, sondern eher ein Quadrat im 2D-Raum enthält. Der Grund dafür ist, dass Sie mehr Operationen durchführen würden, die näher beieinander liegen als in einer Scanlinie.

Dies wird Ihren Speicherverbrauch nicht reduzieren, kann aber einen großen Einfluss auf Seitenwechsel und Leistung haben.

2

Was Sie hier treffen werden, ist virtuelle Adressbereichsgrenze, die mit 32b Windows Ihnen höchstens 2 GB gibt. Sie sollten sich darüber im Klaren sein, dass die Verwendung einer grafischen API wie DirectX oder OpenGL umfangreiche Teile dieser 2 GB für Bildpuffer, Texturen und ähnliche Daten verwendet.

1,5-2 GB für eine 32b-Anwendung ist ziemlich schwer zu erreichen. Der eleganteste Weg, dies zu tun, ist 64b OS und 64b-Anwendung zu verwenden. Selbst mit 64b OS und 32b-Anwendung kann dies etwas sinnvoll sein, solange Sie LARGE_ADDRESS_AWARE verwenden.

Da Sie jedoch Bilddaten speichern müssen, können Sie dies möglicherweise auch umgehen, indem Sie File Mapping as a memory store verwenden - dies kann so erfolgen, dass Sie einen Speicher festgeschrieben und verfügbar haben, aber keine virtuellen Adressen verwenden überhaupt.

0

Wenn Sie genau die Stellen isolieren können, an denen Sie wahrscheinlich große Blöcke zuweisen, können Sie (unter Windows) VirtualAlloc direkt aufrufen, anstatt den Speichermanager zu durchlaufen. Dies vermeidet eine Fragmentierung innerhalb des normalen Speichermanagers.

Dies ist eine einfache Lösung, für die Sie keinen benutzerdefinierten Speichermanager verwenden müssen.

Verwandte Themen