Die Verarbeitung von Haskell-Batchdateien verbessert das Speicherprofil nicht

8

Ich habe einen einfachen Algorithmus zu implementieren: Vergleichen Sie jede Zeile mit jeder anderen Zeile. Jede Zeile enthält eine Zahl und die Vergleichsfunktion ist die Entfernung. Die Summe aller Entfernungen ist das Endergebnis.

Dies kann so einfach wie folgt implementiert werden:

%Vor%

Um vernünftige große Daten in jeder Zeile zu erhalten, habe ich den folgenden Dateigenerator verwendet:

%Vor%

Eine Datei mit 2000 Zeilen von 840 KB benötigt 1,92 Sekunden und 1,5 GB Zuweisung mit einer maximalen Nutzung von etwa 1,5 MB.

Eine 6k-Zeilendatei von 7.5mb dauert 22 Sekunden, 34Gb Zuweisungen, mit einer maximalen Speicherbelegung von etwa 15Mb

Leider werden meine Daten Millionen von Zeilen sein. Ich habe zunächst versucht, die Geschwindigkeit zu verbessern (über die ich in 2 früheren Posts gefragt habe): MapReduce kombiniert mit Iteratee IO ), aber das eigentliche Begrenzungsproblem ist der Raum.

Zwischengedanke : Dies könnte durch Lesen der vollständigen Datei für jede zu vergleichende Zahl überwunden werden. Dies erfordert viel zusätzliche Zeit, da die Datei für jede Zeile, die mit dem Rest der Datei verglichen werden soll, geöffnet und analysiert werden muss. Auch die Anzahl der Speicherzuordnungen wird quadratisch. Also das ist nicht wirklich nützlich als endgültige Lösung

Der letzte Schritt : Das war mein erster Schritt in Richtung auf mein Ziel: dosierte Ausführung. Ich möchte ein paar k Zeilen in Erinnerung behalten. Wenden Sie den ManyToMany-Algorithmus auf diejenigen im Speicher an. Dann durchlaufen Sie den Rest der Datei. In jedem Iterationsschritt muss nur eine Zeile gelesen und analysiert werden, die dann mit allen Elementen im Speicher-Batch verglichen werden kann.

Durch Auswahl einer ausreichend großen Stapelgröße muss die Datei nicht oft neu gelesen werden. Meine Implementierung ist wie folgt:

%Vor%

In der 2k-Zeilendatei mit einer Stapelgröße von 500 wurden 2,16 Sekunden, 2,2 GB-Zuweisungen und etwa 6 MB Speicherplatz benötigt. Das ist 4 mal der Platz der einfachsten Version! Es könnte Zufall sein, aber es werden auch 4 Chargen verwendet ...

Was mich überrascht hat, ist, dass zunächst der gesamte benötigte Platz verbraucht wird, später nimmt der benötigte Platz nur noch ab. Dies wird zu einem Problem mit einer 50k-Zeilendatei (500MB), weil dann der Speicher knapp wird.

Meine Frage ist: Warum verbraucht die Stapellösung mehr Speicher? Es scheint die gesamte Datei für jeden Stapel im Speicher zu behalten, obwohl es (zumindest das ist meine Absicht) nur einen einzigen Stapel im Speicher behalten sollte.

BEARBEITEN : Ich entfernte die Details der 6k-Liniendatei und 500line-Chargen (ich nahm eine falsche Profildatei)

Und hier ist das Raumprofil, das mit der 2k-Liniendatei und den 500-Line-Batches generiert wurde:

EDIT2 : Profilierung mit Halter ergab:

%Vor%

Und das folgende .hp Bild:

EDIT 3: Der vorherige Code alle verwendeten die Pakete:

%Vor%

Wenn ich die Lazy-Versionen von ihnen verwende, ändert sich die gesamte Zeit / Speicher / Raumnutzung nicht wirklich: 2,62 Sekunden, 2,25 GB Zuteilungen und 5,5 MB Speicherplatz

Die akzeptierte Lösung: Die Lazy-Versionen funktionierten nicht, weil hListToEOF eine vollständige Dateilesung erzwang (ich erwartete, dass der Konstruktor träge funktioniert).

Die Lösung besteht darin, folgende Importe zu verwenden:

%Vor%

und in der singleResultBatch Funktion die folgende Änderung:

%Vor%

Dann ändern sich sowohl die Geschwindigkeit (2.72s) als auch die Speicherzuweisungen (2.3GB) nicht, was erwartet wird.

Das Heap-Profil (Speicherplatznutzung) verbessert sich (1,8 MB statt 5,5 MB), wie in:

    
gerben 09.05.2011, 15:09
quelle

2 Antworten

3

Sie müssen Daten inkrementell verarbeiten. Momentan liest hListToEOF alle Daten in einem Durchgang, die Sie dann langsam verarbeiten (daher die anfängliche Speicherspitze, da alles eingelesen wird, dann eine langsame Reduktion, wenn die Liste aufgehoben wird).

Anstatt Ihren eigenen IO über hListToEOF auszuführen, lesen / streamen Sie die Dateien langsam (z. B. mit readFile aus der Text.Lazy-Bibliothek) und ordnen Sie Ihre Verarbeitungsfunktionen diesen zu.

    
Don Stewart 09.05.2011, 17:56
quelle
0

Ich denke, ein großer Teil Ihres Problems besteht darin, dass Sie an Listen festhalten, die im Raum ziemlich ineffizient sind, wenn sie beibehalten werden müssen. Wechseln Sie zu einem kompakteren Speichermechanismus, z. B. Vector . Dies ist eine ziemlich direkte Übersetzung einiger Ihrer Funktionen, mit wahrscheinlich viel Platz für Optimierungen:

%Vor%

Auf meinem System wird dies in einer Datei mit 10.000 Zeilen von Testdaten in etwa 14 Sekunden ausgeführt, mit einer maximalen Speicherbelegung von 125 MB.

    
John L 09.05.2011 17:57
quelle

Tags und Links