Effizientes Lesen einer sehr großen Textdatei in C ++

8

Ich habe eine sehr große Textdatei (45 GB). Jede Zeile der Textdatei enthält zwei durch Leerzeichen getrennte 64-Bit-Ganzzahlen ohne Vorzeichen (siehe unten).

4624996948753406865 10214715013130414417

4305027007407867230 4569406367070518418

10817905656952544704 3697712211731468838 ... ...

Ich möchte die Datei lesen und einige Operationen mit den Zahlen durchführen.

Mein Code in C ++:

%Vor%

Ich bin relativ neu in C ++. Wenn ich diesen Code ausführe, stehe ich vor diesen Problemen.

  1. Da die Datei in Bytes gelesen wird, entspricht die letzte Zeile eines Blocks manchmal einer unvollständigen Zeile in der ursprünglichen Datei ("4624996948753406865 10214" anstelle der tatsächlichen Zeichenfolge "4624996948753406865 10214715013130414417" der Hauptdatei).

  2. Dieser Code läuft sehr, sehr langsam. Es dauert ungefähr 6 Sekunden, um für einen Blockbetrieb in einem 64-Bit-Intel-Core-i7-920-System mit 6 GB RAM zu laufen. Gibt es Optimierungstechniken, mit denen ich die Laufzeit verbessern kann?

  3. Ist es notwendig, in der Boost-Split-Funktion "\ n" zusammen mit Leerzeichen einzufügen?

Ich habe über mmap-Dateien in C ++ gelesen, aber ich bin nicht sicher, ob es der richtige Weg ist. Wenn ja, bitte fügen Sie einige Links hinzu.

    
Pattu 04.11.2014, 13:47
quelle

4 Antworten

14

Ich würde dies so umgestalten, dass es statt in einem Block streamt.

Ein einfacherer Ansatz wäre:

%Vor%

Wenn Sie ungefähr wissen, wie viele Werte erwartet werden, könnte die Verwendung von std::vector::reserve up front es weiter beschleunigen.

Alternativ können Sie eine Memory-Mapped-Datei verwenden und über die Zeichenfolge iterieren.

Update Ich habe das obige Programm modifiziert, um uint32_t s in einen Vektor zu parsen.

Bei Verwendung einer Beispiel-Eingabedatei von 4.5GiB [1] läuft das Programm in 9 Sekunden [2] :

%Vor%

Natürlich weist es mindestens 402653184 * 4 * Byte = 1,5 Gibibyte zu. Also wann Wenn Sie eine 45-GB-Datei lesen, benötigen Sie schätzungsweise 15 GB RAM zum Speichern der Vektor (bei Neuzuweisung keine Fragmentierung vorausgesetzt): Das 45GiB-Parsing beendet in 10min 45s :

%Vor%

Im Vergleich dazu dauerte die Ausführung von wc -l 45gib_uint32s.txt ~ 12 Minuten (ohne Echtzeit-Prioritätsplanung). wc ist blitzschnell

Vollständiger Code für Benchmark verwendet

%Vor%

[1] generiert mit od -t u4 /dev/urandom -A none -v -w4 | pv | dd bs=1M count=$((9*1024/2)) iflag=fullblock > smaller.txt

[2] offensichtlich war dies mit der Datei in den Puffer-Cache unter Linux - die große Datei hat diesen Vorteil nicht

    
sehe 04.11.2014, 14:06
quelle
1

Ich kann nur vermuten, dass der Engpass in:

ist %Vor%

- Weil Sie ein 45MB langes Segment im Speicher reservieren.

Sie sollten die Datei zeilenweise lesen, wie hier beschrieben:

Um Ihr Programm zu profilieren, können Sie clock () zwischen jeder Zeile wie folgt drucken:

Oren Kishon 04.11.2014 13:56
quelle
0

Sie können die Datei im Arbeitsspeicher abbilden, aber das wäre plattformabhängig (unter Unix wäre dies mmap in Windows CreateFileMapping / MapViewIntoFile); Wenn Sie im 32-Bit-System noch Probleme haben, gibt es nicht genügend Speicher für virtuelle Speicher (64-Bit-Systeme hätten dieses Problem nicht).

Die Speicherzuordnung sollte schneller sein als das Lesen der Daten blockweise von der Festplatte.

    
MichaelMoser 04.11.2014 14:15
quelle
0

Unter Linux kann die Verwendung von C <stdio.h> anstelle von C ++ - Streams die Leistung verbessern (da C ++ - Streams über FILE -s erstellt werden). Sie könnten readline (3) oder fgets (3) oder fscanf (3) . Mit setbuffer (3) usw. könntest du einen größeren Puffer (zB 64Kbyte oder 256Kbyte) setzen ... Aber ich denke, Ihr (verbessertes) Programm wäre I / O-gebunden, nicht CPU-gebunden. Dann könntest du mit posix_fadvise (2)

spielen

Sie könnten die Verwendung von Speicherzuordnung mmap (2) & amp; madvise (2) (siehe auch m mode für fopen (3) ). Siehe auch readahead (2)

Wenn Ihr Algorithmus es schließlich zulässt, könnten Sie csplit die Dateien in kleinere Teile zerlegen und jeden davon in parallelen Prozessen verarbeiten.

    
Basile Starynkevitch 04.11.2014 14:40
quelle