2011-01-06 12 views
3

Mein Programm muss Chunks aus einer riesigen Binärdatei mit wahlfreiem Zugriff lesen. Ich habe eine Liste von Offsets und Längen, die mehrere tausend Einträge haben können. Der Benutzer wählt einen Eintrag aus, und das Programm sucht nach dem Offset und liest Längenbytes.Was ist der schnellste Weg, um große Dateien in Delphi zu lesen?

Das Programm verwendet intern einen TMemoryStream, um die aus der Datei gelesenen Chunks zu speichern und zu verarbeiten. Lesen der Daten wird über eine TFileStream wie dies geschehen:

FileStream.Position := Offset; 
MemoryStream.CopyFrom(FileStream, Size); 

Dies funktioniert gut, aber leider wird es immer langsamer, da die Dateien größer werden. Die Dateigröße beginnt bei einigen Megabyte, erreicht aber häufig mehrere zehn Gigabyte. Die gelesenen Stücke sind ungefähr 100 kB groß.

Der Inhalt der Datei wird nur von meinem Programm gelesen. Es ist das einzige Programm, das zu der Zeit auf die Datei zugreift. Auch die Dateien werden lokal gespeichert, so dass dies kein Netzwerkproblem ist.

Ich verwende Delphi 2007 auf einer Windows XP-Box.

Was kann ich tun, um diesen Dateizugriff zu beschleunigen?

edit:

  • Die Access-Datei ist langsam für große Dateien, unabhängig davon, welcher Teil der Datei gelesen wird.
  • Das Programm liest die Datei normalerweise nicht sequenziell. Die Reihenfolge der Blöcke ist benutzergesteuert und kann nicht vorhergesagt werden.
  • Es ist immer langsamer, einen Chunk aus einer großen Datei zu lesen, als einen gleich großen Chunk aus einer kleinen Datei zu lesen.
  • Ich spreche über die Leistung für das Lesen eines Stücks aus der Datei, nicht über die Gesamtzeit, die es dauert, eine ganze Datei zu verarbeiten. Letzteres würde bei größeren Dateien natürlich länger dauern, aber das ist hier nicht das Problem.

Ich muss alle entschuldigen: Nachdem ich den Dateizugriff mit Hilfe eines Memory-Mapped-Datei implementiert, wie vorgeschlagen es stellte sich heraus, dass es nicht viel Unterschied gemacht hat. Aber es stellte sich auch heraus, nachdem ich etwas mehr Timing-Code hinzugefügt habe, dass es nicht der Dateizugriff ist, der das Programm verlangsamt. Der Dateizugriff dauert unabhängig von der Dateigröße nahezu konstant. Ein Teil der Benutzeroberfläche (die ich noch identifizieren muss) scheint ein Performance-Problem mit großen Datenmengen zu haben und irgendwie habe ich den Unterschied nicht gesehen, als ich die Prozesse zum ersten Mal gestartet habe.

Es tut mir leid, schlampig bei der Identifizierung des Engpasses zu sein.

+1

Ich sehe nichts offensichtliches. Diese Stream-Klassen sind nur Wrapper um die Systemdatei-E/A-Funktionen. Wie können Sie die Zugriffsmuster für Random Access erheblich verbessern? –

+0

Sie sagen, dass ein einzelnes Suchen/Lesen für den Benutzer spürbar langsamer ist? Oder dass ein großer "Batch" dieser Operationen langsamer ist? Eine einzelne Such- und Leseoperation sollte ungefähr gleich sein, unabhängig von der Dateigröße, wenn die Daten von der Platte kommen. Bei einer 7200 rpm-Disk sollte das zwischen 5 und 10 ms liegen. –

+0

Es könnte sich um ein Speicherfragmentierungsproblem handeln. Befreien Sie den TMemoryStream zwischen den Operationen? Versuchen Sie, es für die gesamte Lebensdauer der Anwendung am Leben zu halten und zu sehen, ob die Verlangsamung verschwindet. –

Antwort

3

Wenn Sie Hilfethema für CreateFile() WinAPI-Funktion öffnen, finden Sie dort interessante Flags wie FILE_FLAG_NO_BUFFERING und FILE_FLAG_RANDOM_ACCESS. Sie können mit ihnen spielen, um etwas Leistung zu erzielen.

Als nächstes ist das Kopieren der Dateidaten, sogar 100 KB groß, ein zusätzlicher Schritt, der die Vorgänge verlangsamt. Es ist eine gute Idee, die Funktionen CreateFileMapping und MapViewOfFile zu verwenden, um den gebrauchsfertigen Zeiger auf die Daten zu erhalten. Auf diese Weise vermeiden Sie das Kopieren und erhalten möglicherweise auch bestimmte Leistungsvorteile (aber Sie müssen die Geschwindigkeit sorgfältig messen).

+0

zu bekommen Da das Kopieren der Daten unabhängig von der Dateigröße geschieht, kann es nicht der Flaschenhals sein. – dummzeuch

+0

@dummzeuch wer hat es gesagt? Sie erhalten den Zeiger auf zugeordneten Speicher. Sie müssen nicht auf diese Weise kopieren und können direkt auf den zugeordneten Speicher zugreifen. MMF speichert eine Lesung (mindestens) –

+0

Vereinbarte. MMF ist viel schneller als einfache Datei-I/O. In einem meiner Projekte muss ich binäre Protokolldateien öffnen, die bis zu mehrere GB groß sein können. Solche Dateien mit zufälligen Datei-I/O-Dateien zu durchsuchen, kann Minuten dauern, während der gleiche Job mit MMF in einem Bruchteil der Zeit erledigt wird. –

0

Vielleicht kann man diesen Ansatz:

Sortieren Sie die Einträge auf max fileposition und dann auf die folgenden:

  1. die Einträge nehmen, die die erste X MB der Datei nur müssen (bis zu einer bestimmten fileposition)
  2. lesen X MB aus der Datei in einen Puffer (TMemoryStream
  3. lesen Sie nun die Einträge aus dem Puffer (vielleicht multithreaded)
  4. Wiederholen Sie dies für alle die Einträge.

Kurz gesagt: ein Teil der Datei-Cache und alle Einträge lesen, die in sie passen (multhithreaded), zwischenzuspeichern dann den nächsten Teil usw.

Vielleicht können Sie Geschwindigkeit gewinnen, wenn Sie nur Ihren ursprünglichen Ansatz , aber sortiere die Einträge nach Position.

+0

Wird nicht helfen, da die Datei nicht sequentiell gelesen wird. – dummzeuch

0

Der Vorrat TMemoryStream in Delphi ist aufgrund der Speicherzuweisung langsam. Die NexusDB-Firma hat TnxMemoryStream, die viel effizienter ist. Es könnte einige freie da draußen geben, die besser funktionieren.

Die Aktie Delphi TFileStream ist auch nicht die effizienteste Komponente. Wayback in der Geschichte Julian Bucknall veröffentlichte eine Komponente namens BufferedFileStream in einer Zeitschrift oder irgendwo, wo sehr effizient mit Dateiströmen gearbeitet wurde.

Viel Glück.

Verwandte Themen