2016-04-27 18 views
1

Ich mache einige ungewöhnliche Datenmanipulation. Ich habe 36.000 Eingabedateien. Mehr kann dann gleichzeitig in den Speicher geladen werden. Ich möchte das erste Byte jeder Datei nehmen und es in eine Ausgabedatei einfügen, und dann das für die zweite und so weiter tun. Es muss nicht in einer bestimmten Reihenfolge erfolgen. Da die Eingabedateien komprimiert sind, dauert das Laden etwas länger und sie können nicht 1 Byte gleichzeitig gelesen werden. Ich am Ende mit einem Byte-Array jeder Eingabedatei.Zehntausende von Dateien lesen und in Millionen von Dateien in Java schreiben

Die Eingabedateien sind ungefähr ~ 1-6 MB unkomprimiert und ~ .3-1 MB komprimiert (verlustbehaftete Komprimierung). Die Ausgabedateien sind die Anzahl der Eingabedateien in Byte. ~ 36KB in meinem Beispiel.

Ich weiß, die ulimit kann auf einem Linux-Betriebssystem eingestellt werden und das Äquivalent kann unter Windows erfolgen. Obwohl diese Zahl erhöht werden kann, glaube ich nicht, dass OS Millionen von Dateien gleichzeitig schreiben werden.

Meine aktuelle Lösung besteht darin, 3000 oder so gepufferteWriter-Streams zu machen und jede Eingabedatei der Reihe nach zu laden und 1 Byte in 3000 Dateien zu schreiben und dann die Datei zu schließen und die nächste Eingabe zu laden. Bei diesem System muss jede Eingabedatei jeweils etwa 500 Mal geöffnet werden.

Die gesamte Operation dauert 8 Tage und ist nur ein Testfall für eine praktischere Anwendung, die mit größeren Eingabedateien, mehr davon und mehr Ausgabedateien enden würde.

Wenn Sie alle komprimierten Dateien im Speicher abfangen und sie dann nach Bedarf dekomprimieren, klingt das nicht praktisch und würde sich nicht auf größere Eingabedateien skalieren lassen.

Ich denke, die Lösung wäre zu puffern, was ich kann aus den Eingabedateien (weil Speicher Einschränkungen erlauben nicht alles zu puffern), und dann in Dateien sequentiell schreiben und dann noch einmal alles tun.

Allerdings weiß ich nicht, ob es eine bessere Lösung gibt, die etwas verwendet, das ich nicht gelesen habe.

BEARBEITEN Ich bin dankbar für die schnelle Antwort. Ich weiß, dass ich bei der Anwendung meiner Arbeit vage war und ich werde versuchen, das zu korrigieren. Ich habe grundsätzlich ein dreidimensionales Array [Bilder] [X] [Y] Ich möchte über jedes Bild iterieren und speichern Sie jede Farbe von einem bestimmten Pixel auf jedem Bild, und zwar für alle Bilder. Die Probleme sind Speicherbeschränkungen.

Byte [] Pixel = ((DataBufferByte) ImageIO.read (Dateiliste.get (k)) .getRaster(). GetDataBuffer()). GetData();

Dies ist, was ich verwende, um Bilder zu laden, weil es Dekompression kümmert und den Header überspringt.

Ich bearbeite es nicht als ein Video, weil ich einen Rahmen bekommen würde, dann verwandelte es in ein Bild (eine kostspielige Farbraumumwandlung), und wandle es dann in ein Byte um, um Pixeldaten int zu bekommen RGB-Farbraum

Ich könnte jedes Bild laden und teilen Sie es in ~ 500 Teile (Größe von Y) und schreibe in separate Dateien lassen ich offen und schreibe für jedes Bild. Die Ausgänge wären leicht unter einem Gig. Die resultierende Datei könnte vollständig in den Speicher geladen und in ein Array zum sequentiellen Schreiben von Dateien umgewandelt werden.

Die Zwischenschritte bedeutet, dass ich die Last in einem Netzwerk aufteilen konnte, aber ich versuche, es auf einem Laptop niedriger Qualität mit 4 GB RAM, keine GPU und eine niedrige Qualität i7 zu tun.

Ich hatte nicht daran gedacht, irgendwas in der Datei als Zwischenschritt zu speichern, bevor ich davidbaks Antwort gelesen habe. Größe ist die einzige Sache, die dieses Problem nicht trivial macht und ich sehe jetzt, dass die Größe in kleinere handlichere Stücke geteilt werden kann.

+0

nicht sicher, was der Teil 3 ist. Sie müssen eine Datei dekomprimieren und die ersten paar Bytes an eine Datei anhängen? Warum zu 3.000 Dateien? Wenn Sie mehr als 8 Server haben, können Sie hadoop verwenden – tgkprog

+0

Die Eingaben sind alle gleich groß für einen gegebenen Lauf, aber könnte sehr in der Größe zwischen Läufen und auch sehr in der Anzahl der Dateien. Wenn es 1MB pro und 36000 Dateien wären, dann wäre es eine 36GB Datei und das ist das untere Ende der Dinge. Ich konnte diese Datei dann auf sehr vorhersehbare Weise lesen. Jedes Byte, das ich brauche, wäre genau 1MB (die Größe einer Eingabedatei), aber wenn man bedenkt, wie viel Zeit es kostet, eine riesige Datei zu erstellen, ist das wirklich viel schneller? Es würde laden und dann jedes Byte von 36 Gigs in den Speicher löschen, nur um eine Datei zu vervollständigen. Es würde dies 1 Million Mal tun. –

Antwort

5

Drei-Phasen-Betrieb:

Phase eins: Lesen Sie alle Eingabedateien, einen nach dem anderen, und schreiben Sie in eine einzige Ausgabedatei. Die Ausgabedatei wird datensatzorientiert sein - beispielsweise 8-Byte-Datensätze, 4 Byte "Zeichen-Offset" und 4 Byte "Zeichen-Codepunkt". Während Sie eine Datei lesen, beginnt der Zeichenoffset natürlich bei 0, wenn also die Eingabedatei "ABCD" ist, schreiben Sie (0, A) (1, B) (2, C) (3, D) . Jede Eingabedatei wird einmal geöffnet, sequenziell gelesen und geschlossen. Die Ausgabedatei wird einmal geöffnet, fortlaufend geschrieben und dann geschlossen.

Phase zwei: Verwenden Sie eine externe Sortierung, um die 8-Byte-Datensätze der Zwischendatei im 4-Byte-Zeichenoffsetfeld zu sortieren.

Phase drei: Öffnen Sie die sortierte Zwischendatei und führen Sie einen Durchgang durch. Öffnen Sie jedes Mal, wenn sich das Feld für den Zeichenindex ändert, eine neue Ausgabedatei, und schreiben Sie alle Zeichen, die zu diesem Index gehören, in diese Ausgabedatei. Eingabedatei wird einmal geöffnet und sequenziell gelesen. Jede Ausgabedatei wird geöffnet, nacheinander geschrieben und dann geschlossen.

Voilà! Sie benötigen Platz für die Zwischendatei und eine gute externe Sortierung (und Platz für ihre Arbeitsdateien).

Wie @Jorge anmerkt, können sowohl Phase 1 als auch Phase 2 parallelisiert werden, und tatsächlich ist diese Art von Arbeit (Phasen 1 bis 3) genau in mapreduce/hadoops Sweetspot.

2

Sie sind sehr vage drin, aber vielleicht könnte ein Blick auf mapreduce helfen. Es scheint die Art von Job zu sein, der verteilt werden könnte.

Mit den zusätzlichen Informationen, die Sie zur Verfügung gestellt haben, sehe ich wirklich nicht, wie man diese Aufgabe auf gewöhnlicher Hardware wie dem von Ihnen genannten 4GB i7 ausführt. Ihr Problem sieht aus wie ein Bildstapelungsalgorithmus, um aus vielen nicht so guten Bildern ein anständiges Bild zu erhalten, ein typisches Problem bei der astronomischen Bildverarbeitung, und ich bin mir sicher, dass es auf andere Bereiche angewendet wird. Ein gutes Nachschlagen in die astronomische Bildverarbeitung kann eine gute Nutzung Ihrer Zeit sein, es gibt eine Software namens registrax (nicht sicher, ob es noch existiert), die so etwas tut, aber mit Videodateien.

Wenn Sie etwas Mathe in der Serviette machen, wenn Sie 1 Sekunde brauchen, um eine Datei zu öffnen, erhalten Sie nur 10h Dateiöffnung.

Ein Ansatz wäre, etwas FAST Disk (SSD) zu bekommen, würde ich alle Dateien in ein rohes Format dekomprimieren und sie auf der Festplatte speichern, von da an müssen Sie Dateizeiger direkt aus lesen die Dateien, ohne sie in den Speicher zu bekommen und schreiben Sie die Ausgabe in eine Datei, direkt auf der Festplatte.

+0

Dank der Zeiger auf RegiStax (die [noch existiert] (http://www.astronomie.be/registax/)) - ich war mir dieser Kategorie der Bildverarbeitungssoftware völlig nicht bewusst. – davidbak

+0

Froh, dass es geholfen hat! –