![]() |
Delphi-Version: 7
Große Textdateien spitten
Hallo DP Team,
ich habe eine Funktion die große Textdateien in 4 Dateien splittet. Nur diese Funktion ist "sehr" langsam, Sie benötigt für eine 1,5 GB große Textdatei 102 Sekunden. Da die Dateien die mein Programm bearbeiten soll ungefähr in der Größenordnung von 3-4 GB liegen, braucht die Funktion dafür ca 4,5 Minuten und dies ist zu lang. Gibt es eine Möglichkeit oder eine Funktion die das wesentlich schneller machen kann? Wichtig zu wissen ist, dass die Datei nur Zeilenweise gesplittet werden kann. Hier der Funktionscode;
Delphi-Quellcode:
VG
For i := 0 to 3 do
begin AssignFile(F[i],ExtractFilePath(ParamStr(0)) + 'Test_' + IntToStr(i) + '.txt'); ReWrite(F[i]); end; i := 0; AssignFile(ff,Epfad.Text); Reset(ff); while not eof(ff) do begin inc(i); ReadLn(ff, TestText); Case i of 1: WriteLn(F[0],TestText); 2: WriteLn(F[1],TestText); 3: WriteLn(F[2],TestText); 4: WriteLn(F[3],TestText); end; If i = 4 Then i := 0; end; CloseFile(F[0]); CloseFile(F[1]); CloseFile(F[2]); CloseFile(F[3]); Vielen Dank und Grüße |
AW: Große Textdateien spitten
Wie lang sind die Zeilen?
Die einfachste Variante würde eine Vergrößerung des Caches bedeuten. Die Uralten Funktionen AssignFile+TEXT nutzen einen Cache von 128 Byte je Datei und das ist natürlich extrems unökonomisch. (vorallem da alleine schon die Sektoren/Cluster des Datenträgers wesentlich größer sind) Siehe OH von Reset/Rewrite. |
AW: Große Textdateien spitten
Sehe ich das richtig: jede Zeile kommt in eine andere Datei und nach 4 Zeilen fängt es wieder von vorne an? In dem Fall kommst du um ein zeilenweises Lesen, wie es aktuell implementiert ist, wohl nicht herum.
Man könnte allenfalls noch was mit Textbuffern machen: ![]() |
AW: Große Textdateien spitten
Zitat:
Es kann aber sein, dass die Zeilen länger werden, da es sich um eine Fehlerdatei einer Datenintegritätsprüfung einer Datenbank handelt. Hier mal eine Beispielzeile: Warnung: 'Fibu Buch.-Blattname' existiert nicht. Buch.-Blattvorlagenname: ALLGEMEIN, Name: LOHN 02/06 Das Feld Buch.-Blattname in der Tabelle Fibu Buch.-Blattzeile hat eine Tabellenrelation auf das Feld Name in der Tabelle Fibu Buch.-Blattname. 'Fibu Buch.-Blattzeile' ist durch die folgenden Felder und Werte bestimmt: Buch.-Blattvorlagenname='ALLGEMEIN',Buch.-Blattname='LOHN 02/06',Zeilennr.='337000' Zitat:
Mein Programm analysiert diese Zeilen und diesen Prozess will ich beschleunigen indem ich die Datei in 4 gleich große Dateien splitte und in 4 Threads gleichzeitig bearbeite. |
AW: Große Textdateien spitten
Vielleucht könnte
![]() ![]() Ich würde eher versuchen Chunks in M;emory einzulesen, und die dann im Memory zu analysieren. Rollo |
AW: Große Textdateien spitten
Um es zu Beschleunigen willst du es also 5 Dateien lesen und 4 Dateien auf die "langsame" Platte schreiben?
Warum nicht die Datei einmal einlesen und dabei die gelesenen Zeilen direkt an die Threads weiterreichen? |
AW: Große Textdateien spitten
Zitat:
Ich speichere ja die Werte zurück und Überprüfe daraufhin auf doppelte Werte. Da müssen ja dann die threads drauf warten bis die Überprüfung von einem thread fertig ist oder? 🤔 |
AW: Große Textdateien spitten
Wie wäre es denn mit Memory Mapped Files von Herrn Jaenike?
Ließt unheimlich schnell ein und die Zeilen stehen auch schnell zur Verfügung. ![]() |
AW: Große Textdateien spitten
Hab' mal ein bisserl rumgespielt, rausgekommen ist dashier:
Delphi-Quellcode:
Hinter der Konstante Buffer_Size steht jeweils, wielange mein Rechner für die Aufteilung einer Datei von 1.250.375.501 Byte benötigt hat.
program Project1;
{$APPTYPE CONSOLE} uses SysUtils, Classes; const // BUFFER_SIZE = 16384; // 1:19 // BUFFER_SIZE = 65536; // 1:07 // BUFFER_SIZE = 65536 * 2; // 1:03 BUFFER_SIZE = 65536 * 4; // 0:53 // BUFFER_SIZE = 65536 * 8; // 2:07 arLow = 0; arHigh = 3; var i : Integer; f : array[arLow..arHigh] of TextFile; ff : TextFile; inf_buf : array[1..BUFFER_SIZE] of Byte; utf_buf : array[1..BUFFER_SIZE] of Byte; sIn : String; sOut : Array[arLow..arHigh] of String; sZeile : String; dtStart : TDateTime; dtEnde : TDateTime; begin dtStart := Now; WriteLn('Start : ' + DateTimeToStr(dtStart)); sIn := ParamStr(1); For i := Low(f) to High(f) do begin sOut[i] := ExtractFilePath(ParamStr(0)) + 'Test_' + IntToStr(i) + '.txt'; AssignFile(F[i],sOut[i]); SetTextBuf(F[i],utf_buf, BUFFER_SIZE); ReWrite(F[i]); end; i := 0; AssignFile(ff,sIn); SetTextBuf(ff,inf_buf,BUFFER_SIZE); Reset(ff); while not eof(ff) do begin ReadLn(ff, sZeile); WriteLn(F[i],sZeile); inc(i); If i > arHigh Then i := arLow; end; dtEnde := Now; WriteLn('Ende : ' + DateTimeToStr(dtEnde)); WriteLn('Zeit : ' + TimeToStr(dtEnde - dtStart)); WriteLn('Buffer: ' + IntToStr(Buffer_Size)); WriteLn('Dateigroessen:'); WriteLn('Eingabe: ',FileSize(ff) * Buffer_Size,' (',sIn,')'); CloseFile(ff); for i := Low(f) to High(f) do begin WriteLn('Ausgabe: ',FileSize(f[i]) * Buffer_Size,' (',sOut[i],')'); CloseFile(F[i]); end; end. Eventuell kannst Du damit ja was anfangen. |
AW: Große Textdateien spitten
Hallo Nahpets,
Ich konnte damit die benötigte Zeit von 102 Sekunden auf 25 Sekunden verringern. Vielen Dank Start : 29.10.2017 20:59:44 Ende : 29.10.2017 21:00:10 Zeit : 00:00:25 Buffer: 262144 Dateigroessen: Eingabe: 1535639552 (TestDaten\dbtest_PINAG.txt) Ausgabe: 383778816 (Test_0.txt) Ausgabe: 383778816 (Test_1.txt) Ausgabe: 383778816 (Test_2.txt) Ausgabe: 383778816 (Test_3.txt) |
AW: Große Textdateien spitten
Versuch doch trotzdem mal
![]() Das ist sicher noch schneller. |
AW: Große Textdateien spitten
Und wie wäre es mit der TFileStream klasse?
|
AW: Große Textdateien spitten
Zitat:
|
AW: Große Textdateien spitten
TFileStream hab' ich zuerst ausprobiert, war auf meinem Rechner deutlich langsamer (vorausgesetzt, dass ich es vernünftig gemacht haben sollte).
Man muss ja dann im Stream immer nach dem Zeilenumbruch suche und, wenn gefunden, die Ausgabedatei wechseln. Sowas in der Art?
Delphi-Quellcode:
var
output : Array[0..3] of TFileStream; input : TFileStream; i : Integer; ch : Char; begin input := TFileStream.Create(ParamStr(1),fmOpenRead); for i := 0 to 3 do begin output[i] := TFileStream.Create(ExtractFilePath(ParamStr(0)) + 'Test_' + IntToStr(i) + '.txt',fmCreate); end; i := 0; input.Position := 0; input.Read(ch,1); repeat output[i].Write(ch,1); case ch of #10 : begin i := i + 1; if i > 3 then i := 0; end; end; until input.Read(ch, 1) = 0; for i := 0 to 3 do output[i].Free; input.Free; end. |
AW: Große Textdateien spitten
Wieso suchen? Ans Ende springen müsste doch reichen.
|
AW: Große Textdateien spitten
Jetzt gebe ich auch noch meinen Senf dazu...
pragmatischer Ansatz schon mal versucht die Datei im shared readmode für die einzelnen Threads zu öffnen und jedem thread mitzugeben welche wievielte Zeile er zu lesen hat. Also so was wie mytread := Tworkethread.create(onlyLine...) wo dann onlyLine immer der Step ist den er liest also nur 1, 2,3,4... was auch immer Zeile? Dann kannst Du dir die ganze splitterei sparen, die Information ist ja da.......... |
AW: Große Textdateien spitten
Das Problem dürfte dann aber sein, dass du zwar vier Threads hast, aber die Platte nur einen Schreibkopf. ;) Müsste man aber wohl ausprobieren.
|
AW: Große Textdateien spitten
Nachtrag: SSDs könne auch nur an einer Stelle gleichzeitig schreiben. Nur dürfte der Wechsel zu nächsten Speicherzelle schneller sein. Aber das ist ja der allgemeine Fall bei SSDs.
|
AW: Große Textdateien spitten
Zitat:
Ich weiss es jetzt auch nicht genau, aber ich denke wenn Du nur auf einer Datei liest und darum geht es ja letztendlich dürfte der filecache von Windows schon helfen, Es ist letztendlich nur eine Datei offen anstatt 4. Meiner Meinung nach , ungetestet müsste das aber die schnellste Lösung sein. Immerhin reden wir von 4 minuten nur für das erzeugen bevor die threads überhaupt anfangen können...... |
AW: Große Textdateien spitten
Nachtrag 2: Bei HDDs macht es sich ja immer gut, wenn die Buffergröße sich an die Sectorgröße des Dateisystems orientiert. Gibt es so was bei SSDs eigentlich noch?
|
AW: Große Textdateien spitten
Moment. Du schreibst doch in vier? Ich habe jetzt euren Code nicht studiert, aber du hast eine zum Lesen geöffnet und vier zum schreiben. Oder schließt er die zu schreibenden Dateien immer wieder.
|
AW: Große Textdateien spitten
Das Habe ich uach schon überlegt, aber jede zeile gibt nach der Analyse auch einen wert wieder, dieser wird mit den schon bestehenden Werten verglichen und geschaut ob er schon vorhanden ist, wenn nicht wird er hinzugefügt. Problem an 4 threads die jeweils eine Zeile haben ist ja dann, dass die threads warten müssen um auf den Speicher von der Liste zugreifen zu können
|
AW: Große Textdateien spitten
Zitat:
|
AW: Große Textdateien spitten
Zitat:
|
AW: Große Textdateien spitten
Zitat:
1. Zeile -> Datei 1 2. Zeile -> Datei 2 3. Zeile -> Datei 3 4. Zeile -> Datei 4 Und wieder von vorne, bis die Datei vollständig aufgeteilt ist. Irgendwie muss ich also für jede Zeile wissen, wo sie anfängt und wo sie aufhört. Da die Zeilen unterschiedlich lang sind, kann man ja nicht eine bestimmte Anzahl von Zeichen "am Stück" lesen. Die Idee von Fritzew ist aber auch nicht von der Hand zu weisen. Man spart viel Plattenplatz und die entsprechenden Schreiboperationen. Wenn wer jede vierte Zeile verarbeiten muss, kann er ja die übrigen Zeilen überlesen. Und wo die erste Zeile für den Beginn ist, kann man ja mit einem Offset für die am Anfang zu überlesenden Zeilen angeben. Und ob jetzt vier Threads vier Dateien lesen oder vier Threads eine Datei, dürfte für den Schreibkopf auch eher egal sein. Bzw.: Das was bei einer Datei gelesen wird, liegt physikalisch vermutlich näher beeinander, als bei auf mehrere Dateien verteilten Inhalten. Die Sectorgröße ist doch abhängig vom Dateisystem und nicht von der Hardware. Oder irre ich da? Eine Datei wird in 'ner Schleife gelesen und in vier wird geschrieben. Geöffnet wird am Anfang und am Ende alles geschlossen und nicht zeilenweise auf und zu. Aber es wäre doch einen Versuch Wert, die Datei nicht zu splitten, sondern jeweils nur die vierte Zeile auszuwerten. Man muss nur am Anfang angeben, ob keine, eine, zwei oder drei Zeilen zu überlesen sind. |
AW: Große Textdateien spitten
Zitat:
Ob der Thread nun jede x zeile aus einem File oder jede Zeile aus „seinem“ file liest ist doch egal... Ich meine ja gib dem Thread mit welche zeile er zu verarbeiten hat also jeder thread verarbeitet nur jede xte zeile. Wobei x für jeden anderst ist Thread 1 ab erster zeile dann 3überspringen.... Thread 2 ab der 2 zeilw dann 3 überspringen.. Etc.... |
AW: Große Textdateien spitten
Zitat:
|
AW: Große Textdateien spitten
Ah. Ja. Bei der zu lesenden Datei. Ich nehme alles zurück und behaupte das Gegenteil ;)
|
AW: Große Textdateien spitten
Zitat:
falls er eine Zeile nicht analysieren konnte gibt er die Zeile zurück, diese wird in eine Datei geschrieben (Um nicht den Ram unnötig zu füllen, da ja die Dateien 3-4 GB groß sind) Analysierte Zeilen werden zurückgegeben und mit der liste der bereits anderen Analysierten Zeilen verglichen und ggf hinzugefügt. und wenn 4 Threads gleichzeitig 4 Zeilen bearbeiten und dann gleichzeitig die Liste abrufen wollen gibts ja probleme mit dem Zugriff darauf |
AW: Große Textdateien spitten
Zitat:
Im Gegenteil es wird sogar einfacher zu analysieren da es nur einen input gibt |
AW: Große Textdateien spitten
Zitat:
|
AW: Große Textdateien spitten
Darum liest nur Einer die Datei und gibt die Zeilen an die Threads weiter. (1)
Oder man macht z.B. eine CriticalSection um den Lesebefehl, bei jedem Thread. (2) Außerdem ist es blöde ungünstig Einzelzeilen weiterzureichen/zu lesen. Wenn sich jeder Thread (2) mehrere Zeilen holt und sie dann verarbeitet, bis er sich den nächsten Pack holt, oder wenn man mehrere Zeilen z.B. in eine Liste/Array einfügt und diese Listen dann an einen der Threads übergibt (1 ... also Liste abarbeiten und dann Freigeben) oder wenn man jeder Thread schon eine Liste/Array hat und der Lesethread diese Liste dann regelmäßig mit mehreren Zeilen befüllt, wenn die Liste leer oder zu leer ist (2) ... > So kann man die Sperrenden Zugriffe verringern. In neueren Delphis gibt es nicht nur Threads, sondern auch Tasks, da kann man z.B. jeweils 100 Zeilen Lesen und dafür erstellt man eine Task und diese Tasks lässt man dann z.B. in 4 Threads abarbeiten. |
AW: Große Textdateien spitten
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:48 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz