![]() |
Große strukturierte Textdateien laden - immer langsamer
Schönen Abend zusammen,
ich habe ein Problem mit dem Laden von großen Textdateien. Hintergrund: Ich nutze in einem Prog ein eigenes Datenformat aus verschachtelten Arrays, Dictionaries (Hash) und Records. Die Arrays werden durch verschiedene Prozesse mit zahlreichen Daten gefüllt. Um manche aufwendige BErechnungen der Daten nicht mehrfach machen zu müssen, habe ich mir eine eigene Routine geschrieben, mit der die Daten der Reihe nach in eine Textdatei geschrieben und die Datei am Ende mit ZLib komprimiert wird. Das funktioniert auch mit sehr großen Datenmengen sehr gut und schnell (wenige Sekunden). Aso, die Einzeldaten werden mit <Tags> in die Datei geschrieben. Wenn ich die Datei jetzt wieder laden möchte, dauert das exponentiell (!) länger: also nicht nur fast eine Stunde (!), sondern auch von Einzeldatum zu Einzeldatum länger! Praktisch "lade" ich so: - Datei dekomprimieren - Datei in Stringlist laden - via Schleifen und Pos() die Tags einzeln suchen, größere Einheiten kopieren, Einzeldaten darin auswerten und je nach Datentyp in die jeweiligen Container (Array usw.) verschieben. Ich vermute stark, dass es an POS und Copy-Anweisungen liegt, aber habe keine Ahnung, wie ich das optimieren / ersetzen könnte. - Habe aber auch sehr wenig Erfahrung im Umgang mit Datenspeicherung/-laden. Hat jemand einen Rat? Danke und schöne Grüße, Frieder (XE3) |
AW: Große strukturierte Textdateien laden - immer langsamer
Hi,
also damit das wirklich exponenziell geht, da müsstest du schon was ganz falsch gemacht haben - aber kubische Laufzeit ist auch schon nicht toll. zu deinem Problem: Du solltest dir entweder selbst einen Parser schreiben (einmal durchgehen muss reichen!) oder du wechselst das Dateiformat und nimmst einen vorhandenen Parser. Zum Beispiel gibt es viele flotte XML Parser, und du kannst das Format relativ flexibal anpassen. Und so als "goodie" kannst du auch eine XSD schreiben und prüfen ob eine Datei die von dir erwartete Struktur besitzt. Falls du einen einfachen Parser schrieben willst: Da gibt es ganz viele Konzepte und es kann sehr komplex werden. Ich habe auch mal einen geschrieben, leider in java. Habe ihn dir trotzdem mal angehängt. Grob lässt sich das in einen Tokenizer einteilen, der kleine Stücke aus dem Text extrahiert ("Kleinstes Element, das eine Bedeutung hat") und dann einen teil, der die Tokens verarbeitet. In dem Beispiel werden Objekte aus einer Datei gelesen. Jedes Objekt hat dabei seine eigene "aus der Datei laden" Funktion. Der Parser lädt die Datei Zeilenweise und lässt dann einen RegEx auf die Zeile los. Das ist jetzt eine relativ "einfache" Methode, weil der RegEx da einige Arbeit abnimmt die man sonnst selber machen müsste. Heraus kommt ein "Baum" an Objekten in der Variable root.
Code:
Geparst wird ein Text, der ungefähr so aussehen sollte:
package MainPack;
import HelperClasses.TElement; import ItemClasses.*; import java.util.*; import java.util.regex.*; import javax.swing.JOptionPane; public class TParser { ArrayList<TItem> FItemArray; TElement root; /** Creates a new instance of TParser * TParser is used to parse a text file into the abstract hirachial data structure, * implemented by TElement. For simplicity, one root element is created which is not * actually in the file. all elements in the file are then added to this root element * as a child. After this, we can easily unserialise the while tree by talking each of * the root childs and calling its "toMainObject()" Method. This will return a TItem object * which in turn can easily be added o the ArrayList that is returned afterwards. */ public TParser(ArrayList<TItem> AItemArray) { FItemArray = AItemArray; root = new TElement("root"); } public void Parse(Scanner File) { File.useDelimiter(Pattern.compile("[\n]")); Pattern pnewobj = Pattern.compile("^(new T)(.+)\\{"); Pattern pnewchild = Pattern.compile("^(.+)=new T(.+)\\{"); Pattern pstrprop = Pattern.compile("^(.+)=\"(.*)\";"); Pattern pproperty = Pattern.compile("^(.+)=(.+);"); Matcher mnewobj; Matcher mnewchild; Matcher mstrprop; Matcher mproperty; String token = ""; TElement current = null; while (File.hasNext()) // Nächste Zeile laden { token = File.next().trim(); if (!token.equals("")) { mnewobj = pnewobj.matcher(token); mnewchild = pnewchild.matcher(token); mstrprop = pstrprop.matcher(token); mproperty = pproperty.matcher(token); // We now get the tokens one after another, without empty tokens ... if (mnewobj.matches()) // new Item-Object to read { current = root.addRootChild(mnewobj.group(2)); // ^^^^ current also possible, root more error resistant } else if (mnewchild.matches()) // Child-Object { current = current.addChild(mnewchild.group(1), mnewchild.group(2)); } else if (mstrprop.matches()) // String-Property to read { String str = mstrprop.group(2); str = str.replace("(\\\\)*\\n", "\n"); // replace non-escaped \n's with line break str = str.replace("\\\\", "\\"); // replace escaped backslashes with backslash current.addProperty(mstrprop.group(1), str); } else if (mproperty.matches()) // Other Property to read { current.addProperty(mproperty.group(1), mproperty.group(2)); } else // Control-Chars { if (token.matches("\\};?")) // Klammer zu => Ebene hoch current = current.getParent(); else System.err.println("Unrecognised token: \"" + token + "\""); } } } int child = 0; int errors = 0; String messages = ""; for (Object elem : root.getRootChilds()) { try { FItemArray.add(((TElement)elem).readMainObject()); } catch (IllegalArgumentException e) { errors++; messages = messages + "\n" + "root[" + child + "](" + ((TElement)elem).getObjectName() + ")." + e.getMessage(); } child++; } if (errors > 0) JOptionPane.showMessageDialog(null, errors + " Object(s) could not be imported, as properties were invalid:" + messages, "Errors during Import", JOptionPane.ERROR_MESSAGE); } }
Code:
new TYearly{
Title="My Birthday"; Priority=30; DaysInAdvance=1; Description="My Birthday ..."; StartDate=new TDate{ Year=2007; Month=3; Day=16; Time=null; }; EndDate=new TDate{ Year=2026; Month=3; Day=16; Time=null; }; Day=12; Month=3; }; |
AW: Große strukturierte Textdateien laden - immer langsamer
Abend jfheinz,
danke für Deine Antwort. Ich Grunde habe ich mir ja - so denke ich mir - einen Parser geschrieben. Was mich wundert ist: Jeder Parser muss doch mit Suchen und Kopieren arbeiten; was macht dann mein Auslesen so viel langsamer? Ich hatte das Problem schon öfters; sobald eine stringlist etwas größer wird und man sie sukzessive durcharbeiten muss (via POS/Copy), wird es bald sehr langsam.. Gibt es eine andere Alternative? Schöne Grüße, Frieder |
AW: Große strukturierte Textdateien laden - immer langsamer
Zitat:
![]() Der schafft das Lesen und Parsen eines Registry-Exportes mit ca. zwei Dritteln der sequentiellen Festplattengeschwindigkeit. Ein anderer Parser, den ich beruflich für eine eigene Skriptsprache geschrieben habe, arbeitet auch komplett pointerbasiert und ist dadurch sehr schnell. Denn der Quelltext an sich bleibt wo er ist, zur Analyse wird nur ein PChar-Pointer verschoben und das auch nur in eine Richtung, sprich Single-Pass. |
AW: Große strukturierte Textdateien laden - immer langsamer
Ah, ok, jetzt verstehe ich das mal. Tja, in meinem Fall hätte ich keine Ahnung, wie ich da mit Pointern arbeiten soll -, dafür fehlt mir die Erfahrung.. Ich kannte Pointer bislang nur insofern, als damit ein Zeiger auf ein komplettes Datum (also z.B. eine Integer, ein String o.ä.) verschoben werden kann. Wie aber soll man *innerhalb* eines Datums (wie eine Textdatei) einen Zeiger verschieben? Habt Ihr da zufällig einen Link fürs erste Reinschauen zur Hand?
Danke und schöne Grüße, frieder |
AW: Große strukturierte Textdateien laden - immer langsamer
Sorry, dass ich nochmal nachfrage: Ich lese gerade nochmal über Datenspeicherung via Streams. Könnte das Laden via Streams schneller gehen anstelle via Textdateien?
Danke und schönen Abend, frieder |
AW: Große strukturierte Textdateien laden - immer langsamer
Die schnellste Variante sind MMFs wie ich sie in meinem Programm auch nutze. ;-)
Schau es dir einfach mal an, das ist nicht so kompliziert. |
AW: Große strukturierte Textdateien laden - immer langsamer
Hallo,
Zitat:
Der "Trick" ist, dass der Puffer immer nur klein ist. In meinem Fall habe ich das über die Zeilen realisiert: Da die Datei Zeilenweise verarbeitet wird, habe ich immer nur einen Text von vll. 30 Zeichen, der einem Token zugeordnet werden muss. |
AW: Große strukturierte Textdateien laden - immer langsamer
Zitat:
Beispiel:
Delphi-Quellcode:
Oder:
var
MyString: String; MyPtr: Pointer; begin MyString := 'Hallo Welt'; GetMem(MyPtr, Length(MyString)); try Move(MyString[1], MyPtr^, Length(MyString)); // "Hallo Welt" steht nun im Pointer Finalize(MyString); SetLength(MyString, 5); Move(MyPtr^, MyString[1], 5); // "Hallo" wird aus dem Pointer kopiert finally FreeMem(MyPtr); end; end;
Delphi-Quellcode:
var
MyString: String; MyPtr: PChar; i, j: Integer; Found: Boolean; begin MyString := '12345|Test'; GetMem(MyPtr, Length(MyString)); i := Integer(MyPtr); // Addresse vom Pointer merken try Move(MyString[1], MyPtr^, Length(MyString)); Found := FALSE; j := 1; repeat if MyPtr^ = '|' then Found := TRUE; // Trennzeichen gefunden, irgendwas machen Inc(MyPtr^); Inc(j); until Found or (j > Length(MyString)); finally MyPtr := PByte(i); // Addresse vom Pointer wiederherstellen FreeMem(MyPtr); end; end; |
AW: Große strukturierte Textdateien laden - immer langsamer
Na ja, Leute. Wenn mit 'Pointer' wirklich ein PChar gemeint ist, dann ist das 'Optimieren auf hohem Niveau'. Damit kann man sein Programm um einen Faktor X (so 10% schätze ich, von mir auch auch 20%) schneller machen. Auch der wirklich sinnvolle Hinweis, mit MMF einzulesen, bringt bei sehr großen Dateien eine drastische (aber konstante) Verbesserung (wieder um einen Faktor).
Hier geht es aber zunächst um das Verfahren. alsp das 'wie'. Die Vorredner haben hier Recht: Einen Parser kann man mit einer Komplexität von O(n) hinbekommen, d.h. er verhält sich von der Laufzeit so, das er bei einer Verdoppelung der Inputlänge auch nur doppelt so lange für die Verarbeitung braucht. Dein Algorithmus scheint bei mindestens O(n^2) zu liegen (d.h. er vervierfacht die Zeit bei Verdopplung der Input-größe), aber wie sollen wir wissen, wie wir dir helfen können, wenn wir noch keine Zeile Code gesehen haben? Zitat:
|
AW: Große strukturierte Textdateien laden - immer langsamer
Wie wäre es mit einer Verwaltungsstruktur am Anfang/Ende der Datei, oder in einer eigenen Datei, welche die Offsets,Länge und den Typ der Objekte beinhaltete. Die Objekte selbst werden dann gezielt per passendem Streamreader/writer geschrieben und gelesen?
|
AW: Große strukturierte Textdateien laden - immer langsamer
Zitat:
Beispiel:
Delphi-Quellcode:
Mit Inc und Zeichenzugriff via MyPtr^ lässt sich das bequem machen. Untypisiert mit dem Typ Pointer macht doch gar keinen Sinn...
var
MyString: String; MyPtr: PChar; begin MyString := 'Hallo Welt'; MyPtr := PChar(MyString); ShowMessage(Copy(MyPtr, 1, 5)); // 'Hallo' Inc(MyPtr, 6); ShowMessage(MyPtr); // 'Welt' end; |
AW: Große strukturierte Textdateien laden - immer langsamer
Zitat:
Nimm da lieber SetString. Zitat:
Menno, ich finde es nicht mehr, aber ich dachte ich hätte mal eine Funktion gesehn (direkt im Delphi), welche einen String nimmt und einem daraus einen PChar erzeugt. Also das GetMem und Move direkt verbaut, wo man dann nicht an Unicode denken muß. |
AW: Große strukturierte Textdateien laden - immer langsamer
Meinst Du
![]() |
AW: Große strukturierte Textdateien laden - immer langsamer
Zitat:
|
AW: Große strukturierte Textdateien laden - immer langsamer
Hmm.... Ok, der Unterschied zwischen O(n) und O(n^2) ist wohl doch Pchar statt String...
Hmmpfrl. |
AW: Große strukturierte Textdateien laden - immer langsamer
Schutz des Programms?
Früher konnte man höchstens Controls aus der DFM entfernen, wenn man die Resourcen der EXE bearbeitet, aber dank CodeBinding kann man glatt die komplette GUI umbauen/erweitern, nur indem man die DFM bearbeitet. Zitat:
Delphi-Quellcode:
:zwinker:
SizeOf(Char)
|
AW: Große strukturierte Textdateien laden - immer langsamer
Zitat:
|
AW: Große strukturierte Textdateien laden - immer langsamer
Hab's gefunden (in falscher Unit gesucht :oops:)
![]() Zitat:
Ich mein eher sowas sie DupStr/StrDup aus C++. Nja, aber StrLCopy (weil ja nicht der ganze String kopiert werden soll), zusammen mit einem GetMem würde sich gut in einer Funktion machen.
Delphi-Quellcode:
function DupStr(str: PAnsiChar; MaxLen: NativeInt = -1): PAnsiChar; override; // MaxLen incl. #0
function DupStr(str: PWideChar; MaxLen: NativeInt = -1): PWideChar; override; function DupStr(str: PAnsiChar; MaxLen: NativeInt = -1): PAnsiChar; begin if MaxLen = 0 then Exit(nil); if MaxLen < 0 then MaxLen = StrLen(str) + 1; Result := GetMemory(MaxLen * SizeOf(str^)); try StrLCopy(Result, str, MaxLen); finally FreeMemory(P); end; end; function DupStr(str: PWideChar; MaxLen: NativeInt = -1): PWideChar; ... ![]() ![]() ![]() DupStr = von PChar/String zu PChar |
AW: Große strukturierte Textdateien laden - immer langsamer
Abend zusammen,
wow, :shock: - da hab ich ja ne Diskussion losgetreten.. Vielen Dank für die vielen Anregungen! Ich versuche mal die Möglichkeiten für eine Optimierung zu sortieren: - Ohne Pointer: soviel POS wie möglich raus, Stringlist möglichst nur einmal durchlaufen, ggf. kleinere Puffer bilden. -> Das war bisher in etwa auch mein Ansatz. Das Problem ist dabei aber die Datencodierung a la 'Quasi'-XML, also mit Anfangs- und Endtag, fortlaufend, Baumstruktur. Bisher habe ich noch nicht mit fertigen XML-Parsern gearbeitet. - Mit Pointern: Vielen Dank für die Code-Bsp. Ich muss aber leider gestehen, dass ich sie nicht wirklich verstehe. Mir ist wohl immer noch nicht die Logik klar, mit der man "Pointer verschieben" kann u.ä. Ohne Pointer, zB bei Strings ist mir klar: Ich kann mit einem Index und einem Count Teile eines Strings kopieren, löschen etc, quasi Intervalle herausschneiden und in einen neuen Topf gegeben und weiterverarbeiten. Wie soll das bei Pointern gehen? Wie kann ich da 'Intervalle' von Daten definieren, wenn ich doch nur mit EINEM Zeiger auf EINEN 'Punkt' (also z.B. eine ganze Variable im Speicher) verweise? Bräuchte ich - für Intervalle wie '234' in '12345' nicht zwei Pointer? Da steh ich aufm Schlauch.. - Streams: Das scheint mir die naheliegendste, weil auch gerade am einfachsten umzusetzende Lösung zu sein. Die Reihenfolge der Datenspeicherung/-ladung ist klar. Ob das dann wirklich schneller wird, weiß ich noch nicht. Muss ich ausprobieren. Es dürften damit aber einige POSes entfallen. Falls noch jemand einen Tipp zum Pointer-Verständnis hat (gerne auch am Bsp. von jaenicke), würde ich mich freuen. Ansonsten nochmal herzliches Dankeschön und einen schönen Sonntagabend wünscht Euch frieder |
AW: Große strukturierte Textdateien laden - immer langsamer
Ja, du siehst das richtig was Pointer angeht. Ich habe z.B. immer einen Startpointer für den aktuellen Bereich und einen Laufpointer. Habe ich dann ein Element identifiziert, kopiere ich einfach ab dem Startpointer den String heraus. Die Länge bekomme ich durch die Differenz mit dem Laufpointer.
Auf diese Weise muss nur etwas kopiert werden, wenn ich auch etwas herauskopieren will. Ein Beispiel kann ich erst morgen Abend liefern, wenn nötig. |
AW: Große strukturierte Textdateien laden - immer langsamer
Ok, danke!
Ich habe mich jetzt dran gemacht und das ganze zunächst als FileStream / StringToStream zu lösen. Leider habe ich jetzt das Problem, dass offenbar die Strings falsch geschrieben und/oder gelesen werden: Wenn ich "True" schreibe, kommt zumindest (spätestens) beim Lesen ein "TrXX" (XX = komische Zeichen). Ich vermute, es ist ein Codierungsproblem (PChar usw.). Habe dahingehend schon im Forum gesucht, aber die Lösungsvorschläge dort (für D2009) haben nicht funktioniert. Mein Code für das Speichern/Lesen der Strings und Integer:
Delphi-Quellcode:
Ich nutze Delphi XE3 - und die Strings sind von 0 Zeichen bis sehr große Datentabellen-Strings (und sollten eigentlich überall AnsiString sein).
Function StreamToString(aStream: TStream): string;
var Len: longint; tempstr: String; begin aStream.Read(Len, SizeOf(Len)); SetLength(tempStr, Len); aStream.Read(PChar(tempStr)^, Len); result:= tempStr; end; Function StreamToInteger(aStream: TStream): integer; var tempint: integer; begin aStream.Read(tempint, SizeOf(tempint)); result:= tempint; end; //---------- Procedure StringtoStream(aStream: TStream; astring: string); var len: longint; begin len:= Length(astring); aStream.Write(len, Sizeof(Len)); aStream.Write(PChar(aString)^, Len); end; Procedure IntegertoStream(aStream: TStream; aint: integer); begin aStream.Write(aint, SizeOf(aint)); end; Wenn ich anstelle von PChar einfach AnsiChar nehme, bekomme ich einen Error für ungültige Typumwandlung. Danke und schöne Grüße, frieder |
AW: Große strukturierte Textdateien laden - immer langsamer
Dennoch nimm PAnsiChar und AnsiString.
String/PChar hat seit Delphi 2009 zwei Byte pro Zeichen und in deinen Dateien ist es aber nur mit 1 Byte pro Zeichen gespeichert. Und was ist die "entgültige" Typumandlung? PS: Bezüglich der Unicodeumsellung gibt es zahlreiche Threads und Tutorials. Grundsätzlich kann man sagen, daß es deine eigene Schuld war, daß jetzt nichts mehr läuft, denn man sollte niemals dynamische Typen binär speichern/übertragen, sonsdern ausschließlich generische Typen, welche unveränderlich sind. Das fing bei Unicode an, wo tring, Char und PChar sich veränderten und geht nun mit 64 Bit weiter. |
AW: Große strukturierte Textdateien laden - immer langsamer
@friedemann: Mal was gänzlich anderes. Warum benutzt du eigentlich nicht gleich eine ordentliche Datenbank, wie zB Firebird, um deine Sachen abzulegen? :mrgreen:
|
AW: Große strukturierte Textdateien laden - immer langsamer
Zitat:
Zitat:
Habe AnsiChar einsetzen können, jetzt läuft alles. - Durch das Abspeichern/Laden der Daten via Streams geht der Prozess insgesamt deutlich schneller. Danke Euch allen für die Anregungen und Tipps, schöne Woche, frieder |
AW: Große strukturierte Textdateien laden - immer langsamer
Zitat:
Wobei es auch anders kommen kann. Bei 64 Bit war mal der Integer/Cardinal veränderlich und nun hat man den einfach eingefroren und einen neuen Typen erfunden. Das gibt jetzt also beim Speichern/Laden/Übertragen weniger Problemem aber dafür dann im Programm, weil ganz Viele zwischen Pointer und Integer gecastet haben (was eigentlich auch gepaßt hätte), aber nun eben Datenverlust mit sich bringt, weil vom Pointer die Hälfte verloren geht. PS: Ich versuche hier grade ein Projekt "eigentlich" auf 64 Bit zu portieren, aber was darin alles gemacht wurde ... da wird einem Schlecht. z.B. wurden erstmal alle Warnungen deaktiviert. OK, die Meisten Warnungen sind nur String<>AnsiString-Warnungen, wo Delphi es aber automatisch npasst, aber dazwischen waren auch welche, wo Delphi nichts anpassen konnte. PChar<>PAnsiChar und dann wundert man sich, daß Einiges nicht geht :wall: Also immer schön auf den Compiler hören. Nja, einfacher hättest du es mit TReader/TWriter gehabt. Das beachtet selbst das Format und speichert es sogar in der Datei mit ab, so daß man die Datei auch noch auslesen könnte, selbst wenn etwas nicht paßt. Das kennst du z.B. von der DFM und wenn es gewisse Komponenten oder Property nicht mehr gibt. |
AW: Große strukturierte Textdateien laden - immer langsamer
@Himitsu
nun laß mal gut sein. Auch wenn Du Recht hast, das "Typ-Problem" ist doch hausgemacht. Nimm doch einfach den "integer". Das ist eine ganze Zahl mit Vorzeichen, und je nach Compiler/Rechner 8/16/32/64 Bit breit das kann man doch nicht ernst nehmen. Mit Rücksicht auf die lernschwächeren unter uns ist alles Integer und Char ? Solange in den Hanbüchern noch diese Verdummung betrieben wird wirst Du auch immer die gleichen Probleme haben. (was ist gegen word16,word32,word64 einzuwenden?) Gruß K-H |
AW: Große strukturierte Textdateien laden - immer langsamer
Es scheinen ja hier mehrere Probleme eine schöne Summe von Performance zu kosten. Ein Teilproblem ist sicherlich die Verwendung von Stringlisten. Wir hatten auch Dateien auszulesen, und haben dabei numerische "Tags" als Strings eingelesen und als Sortierschlüssel für die Stringlisten verwendet. Stringvergleiche sind aber langsam...so richtig langsam. Deshalb sind wir auf Arrays umgestiegen in denen besagte "Tags" wirklich numerisch vorliegen, das flitzt jetzt erheblich schneller. Wenn also Deine Listen sortiert sind/sein müssen, dann empfehle ich von Strings als Sortierkriterien wegzugehen, oder einen effizenteren Vergleichsalgorithmus zu finden.
Sherlock |
AW: Große strukturierte Textdateien laden - immer langsamer
Solange wir nicht wissen, wie der Source wirklich aussieht, sind das alles nur gutgemeinte Ratschläge.
Und der Unterschied zwischen gut gemeint und gut gemacht ist bekanntlich gewaltig. Gruß K-H |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:00 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 by Thomas Breitkreuz