![]() |
TStringlist mit 60000 Einträgen zu langsam
Liebe Leute,
ich habe eine Liste in einer Stringlist mit teilweise über 60-100.000 Einträgen, die ich derzeit der Reihe nach abarbeiten muss. In jeder Zeile sind mit TAB getrennt 3 Wörter, von denen immer ein bestimmtes herausgezogen und in eine neue Stringlist übertragen wird. Mein Problem: Wenn ich die Liste durchgehe, dauert es ewig: >5 Minuten für 30000 Einträge (Netbook mit 1,4 Ghz und 1GB RAM). Hat jemand eine Idee, wie ich das (SEHR) viel schneller hinbekomme? Danke für Eure Mühe und Hilfe, viele Grüße, friedemann PS: Delphi 5 |
Re: TStringlist mit 60000 Einträgen zu langsam
Umstellen auf TListView mit virtuellen Modus
Umstellen auf VirtualStringTree mit virtuellen Modus Umstellen auf TElTreeStringgrid (mit virtuellen Modus) |
Re: TStringlist mit 60000 Einträgen zu langsam
Was du auch noch probieren könntest, wäre vor dem Abarbeiten bei beiden Stringlists ein
Delphi-Quellcode:
aufzurufen. Nach dem Abarbeiten nicht das
StringList.BeginUpdate;
Delphi-Quellcode:
vergessen! (Kann dir leider im Moment nicht sagen, ob Begin- bzw. EndUpdate einen Geschwindigkeitsvorteil für dich bringt).
StringList.EndUpdate;
mfg |
Re: TStringlist mit 60000 Einträgen zu langsam
@Bernhard: StringList und nicht StringGrid (ich geh mal davon auß, daß er sich da nicht verschrieben hat)
Wie verarbeitesten du denn diese Liste und wie groß ist diese (der Text in Byte)? 100.000 Einträge ist jetzt nicht sehr viel. Erstmal könntest du den langsamen Speichermanager von Delphi austauschen > ![]() (in neueren Delphis wurde er schon ausgetauscht) Und jetzt kann man erstmal nur noch deine Verarbeitung versuchen zu optimieren, aber diese kenne wir noch nicht. PS: BeginUpdate bringt bei einer reinen/alleinstehenden StringListe nicht viel, da dieses OnChange-Ereignisse und ähnliches, wie GUI-Aktualisierungen unterbindet, welche es bei einer enzelnen TStringList nicht unbedingt gibt. |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
|
Re: TStringlist mit 60000 Einträgen zu langsam
Moin, moin,
Zu Listen gibt es in der DP einen interesanten Thread: ![]() Grüße // Martin |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo,
ich würde mal MemCheck (oder FastMM) nehmen, dann die Liste füllen und das Programm ohne List.Free beenden. Dann bekommst du genau raus, wie viel Speicher die Liste braucht. Oder für grobere Schätzung den Task-Manager nehmen. Die 1 GB RAM könnten schon das Problem sein, wenn Delphi auch noch offen ist. Zumal du ja einen Teil der Strings in eine 2. Liste packst, die dann auch noch Speicher frißt (ähem isst ;) ). Heiko |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Wenn ich in blaue rate, würde ich sagen, daß das Benutzen der StringListe selbt auch schon ein Fehler ist. Du sagst in jedem Listeintrag steht ein String, der mit TAB getrennt drei Wörter enthält und du nur diesen String auseinandernimmst, um an deinen gewünschten String zu kommen. Du solltest eher eine Datenklasse mit drei Feldern benutzen und diese dann in TObjectList schreiben. Der Vorteil der Datenklasse ist, daß du nicht mühsam etwas aueinander nimmst, sondern direkt den gewünschten Wert hast. Beispiel:
Delphi-Quellcode:
Alles nur ein Beispiel. Ich denke das wird dein Problem sein.type TMeineDaten = class public property Bezeichner: String; property Strasse: String; property Ort: String; end; .... .... var myList: TObjectList; ... neueDaten := TMeineDaten.Create(); neueDaten.Bezeichner := "Stefan"; ... myList.add(neueDaten); ... ... for i := 0 to myList.Count-1 do begin datenAusListe := TMeinDaten(myList[i]); Edit1.Text := datenAusListe.Bezeichner; .... end; ... |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Je nach Aktionen die du auf der Stringlist (Suchen, IndexOf, ...) machst wäre eine Hash-Table oder B-Tree günstiger. |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Acer nettop mit 1,6 Ghz und 1GB Ram gute 3 Sekunden, die Liste enthält danach ca. 65MB Daten. Das durchsuchen der Stringliste nach einem ebenfalls zufällig generierten Teilstring mit anschliessendem umkopieren eines viertels der Liste in eine neue Liste in zufälligen Abständen dauert weniger als 1 Sekunde. Am Rechner und an TStringlist kann es nicht liegen... |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Und wenn er die Liste nur durcharbeitet ... einen Eintrag nach dem anderem ... da braucht er keine Hashlisten, wobei hier eine Hashliste sogar einen Hauch langsamer wäre. Irgendwann müssen die Hashs berechnet werden, welche dann nichtmal nötig wären, wenn man eh nicht sucht. |
Re: TStringlist mit 60000 Einträgen zu langsam
Na Ihr spökenkieker,
Laßt Friedemann doch erst einmal erzählen was er da treibt, ich spekuliere auf etwas csv-ähnliches. Gruß K-H |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Ich denke, man kann die einzelnen Bestandteile in maximal 300ms (3x Copy, 2x Pos + overhead) auseinanderfriemeln. Woher kommt bloß das Märchen, das eine TStringlist ein Performancekiller ist? Es handelt sich um ein dynamisches Array von (String,Objekt)-Tupeln. Die dynamische Vergrößerung erfolgt nach einer sehr schnellen Heuristik. Natürlich kann man das schneller machen, und sollte es vielleicht sogar. Aber hier geht es erstmal um Grundlegendes. |
Re: TStringlist mit 60000 Einträgen zu langsam
Just for Info
Es gibt viele Klassen, die von TStrings abgeleitet sind. z.B. TStringList, TMemoStrings, TComboBoxStrings, TListBoxStrings, ... Der Knackpunkt ist, dass z.B. TMemoStrings ungefähr um den Faktor 50 langsamer ist als TStringList. Wenn man also mit einem TMemo arbeitet und das Property Lines benützt, ist das um Welten langsamer, als wenn man TStringList direkt benützt. |
Re: TStringlist mit 60000 Einträgen zu langsam
Abend zusammen,
zuerst mal vielen vielen Dank für die tollen Antworten. Ich versuche mal drauf zu reagieren: 1) FastMM - habe ich jetzt eine viertel Stunde gegoogelt und leider keine brauchbare Info bekommen, die mir erläutert, was ich damit konkret anfangen könnte. Hat jemand da zufällig einen geeigneten Thread im Kopf? 2) Bisschen Code. Ich bin ein fürchtlich schlechter Coder, daher verzicht ich am Anfang immer auf Code senden. Aber ich sehe ein, dass ich anders nicht voran käme. Zuvor kurz die Info: Die Textdateien bestehen zuerst aus langen Listen, z.B. ("[tab]" = Tabulator): Die [tab] ART [tab] D Häuser [tab] NN [tab] Haus usw. Diese Liste wird in eine Stringlist1 geladen, dann String für String durchgegangen, in Abhängigkeit von bestimmten Infos entweder die erste, zweite oder dritte Stelle ermittelt und die dann in eine neue Stringlist2 kopiert. Sind alle Strings von Stringlist1 abgearbeitet, werden die einzelnen Strings aus Stringlist2 noch direkt aneinander gefügt (wieder zu einem Fließtext). Letzteres habe ich direkt versucht, aber leider nicht hinbekommen; das zieht aber auch kaum Zeit; die meiste Zeit geht drauf fürs Durchgehen von Stringlist1. Hier ein Codeausschnitt aus einem Programmteil, der nicht mehrere Dateien, sondern nur einen Ausschnitt aus einer Datei verarbeitet ("Preview"-funktion); das Prinzip müsste daraus aber klar werden:
Delphi-Quellcode:
// Einzelne Texte zusammensetzen
begin quelle:= tstringlist.create; ziel:= tstringlist.create; try //Previewdatei laden quelle.LoadFromFile(extractfilepath(application.exename) + 'preview2.dat'); //Ersetzen von zwei Zeichen, da sich ansonsten im weiteren Analyseverlauf nicht korrekt verarbeitet werden; umständlich, aber anders weiß ichs nich.. quelle.Text:=stringreplace2(quelle.text, '"', 'ANFUEEEE'); quelle.Text:=stringreplace2(quelle.text, #39, 'EINFANFUEEEE'); for ii:=0 to quelle.Count-1 do begin wortarttemp:= gibmirwortart(quelle.strings[ii], #9); //Token zusammennehmen if pos('#' + wortarttemp + '#', tok)<>0 then //Bedingung; braucht keine Zeit, da der zu durchsuchende String tok nur ~40 Zeichen groß ist ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); //Lemma zusammennehmen if pos('#' + wortarttemp + '#', lem)<>0 then //s.o. begin lemmareal:= gibmirlemma(quelle.strings[ii], #9); schon:= 0; if (lemmareal= '<UNKNOWN>') and (checkbox2.checked) then //weitere Bedingungen begin ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); schon:= 1; end; if (lemmareal= '@card@') and (checkbox4.checked) then begin ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); schon:= 1; end; if (lemmareal= 'CARD') and (checkbox4.checked) then begin ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); schon:= 1; end; if (lemmareal= '@ord@') and (checkbox4.checked) then begin ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); schon:= 1; end; if schon=0 then ziel.text:= ziel.text + lemmareal; end; //Wortart zusammennehmen if pos('#' + wortarttemp + '#', poss)<>0 then //s.o. ziel.Text:= ziel.text + gibmirwortart(quelle.strings[ii], #9); end; //Wenn die Stringlist quelle durchgearbeitet ist und aller relevanten Strings in ziel, dann sollen die Strings in ziel zu einem fortlaufenden Text (-> zielende: string;) zusammengesetzt werden for x:=0 to ziel.Count-1 do zielende:= zielende + ' ' + ziel.Strings[x]; //Vorherige Ersetzungen rückgängig machen zielende:= stringreplace2(zielende, 'ANFUEEEE', '"'); zielende:= stringreplace2(zielende, 'EINFANFUEEEE', #39); //Ergebnis (Preview) in Memo ausgeben memo2.text:= zielende; finally quelle.free; ziel.Free; end; Der vorangegangene Code verweist auf folgende Routinen, die ich in Anlehnung an Funktionen ausm Netz verwende (hier hängt wohl die meiste Zeit..):
Delphi-Quellcode:
//Funktion für die Ersetzung; ist schneller als die alte stringreplace function stringreplace2(aString, FromStr, ToStr: AnsiString): AnsiString; var I: Integer; begin // check whether string are equal if FromStr = ToStr then begin Result := aString; Exit; end; Result := ''; // find fromstr I := Pos(FromStr, aString); while I > 0 do begin // copy all characters prior fromstr if I > 1 then Result := Result + Copy(aString, 1, I - 1); // append tostr Result := Result + ToStr; // delete all until after fromstr Delete(aString, 1, I + Length(FromStr) - 1); // find next fromstr I := Pos(FromStr, aString); end; Result := Result + aString; end; //hier wird die zweite Stelle des durch tab geteilten Strings aus quelle ermittelt function gibmirwortart(s:string; sep:char) :string; var t: Tstringlist; begin //hier muss jetzt das zweite Wort rausgefiltert werden t:= tstringlist.create; try extractstrings([char(sep)], [' '], pchar(s), t); result:= t.Strings[1]; finally t.free; end; end; //hier wird die erste Stelle des durch tab geteilten Strings aus quelle ermittelt function gibmirToken(s:string; sep:char) :string; var t: Tstringlist; begin //hier muss jetzt das zweite Wort rausgefiltert werden t:= tstringlist.create; try extractstrings([char(sep)], [' '], pchar(s), t); result:= t.Strings[0]; finally t.free; end; end; //hier wird die dritte Stelle des durch tab geteilten Strings aus quelle ermittelt function gibmirLemma(s:string; sep:char) :string; var t: Tstringlist; begin //hier muss jetzt das zweite Wort rausgefiltert werden t:= tstringlist.create; try extractstrings([char(sep)], [' '], pchar(s), t); result:= t.Strings[2]; finally t.free; end; end; 3. Hashs u.ä. kann ich leider nicht umsetzen, dafür reicht mein Anfängerwissen nicht.. Vielleicht habt ihr auf meiner Codebasis eine Idee zum Zeitsparen? Danke für Eure Mühe und viele Grüße zum Abend, Friedemann |
Re: TStringlist mit 60000 Einträgen zu langsam
FastMM gibs hier:
![]() binde es einfach mal ins projekt ein und schau obs schneller geht. und schau dir mal die faststrings.pas an: ![]() |
Re: TStringlist mit 60000 Einträgen zu langsam
Delphi-Quellcode:
Versuch es erstmal hiermit.
//hier wird die erste Stelle des durch tab geteilten Strings aus quelle ermittelt
function gibmirToken(s:string; sep:char) :string; begin Result := Copy(s, 1, Pos(#9, s) - 1); end; //hier wird die zweite Stelle des durch tab geteilten Strings aus quelle ermittelt function gibmirwortart(s:string; sep:char) :string; begin Delete(Result, 1, Pos(#9, s)); Result := Copy(Result, 1, Pos(#9, s) - 1); end; //hier wird die dritte Stelle des durch tab geteilten Strings aus quelle ermittelt function gibmirLemma(s:string; sep:char) :string; begin Delete(Result, 1, Pos(#9, s)); Delete(Result, 1, Pos(#9, s)); end; Mit Hilfe von PosEx ließe sich auch dieses noch weiter optimieren. Statt diesem "schon" und dem Ausführen von Abfragen, welche eh nie zuschlagen werden, da vorher schon eine Abfrage erfolgreich war, solltest du dich eventuell mal mit dem ELSE beschäftigen.
Delphi-Quellcode:
// Einzelne Texte zusammensetzen
begin quelle:= tstringlist.create; ziel:= tstringlist.create; try //Previewdatei laden quelle.LoadFromFile(extractfilepath(application.exename) + 'preview2.dat'); //Ersetzen von zwei Zeichen, da sich ansonsten im weiteren Analyseverlauf nicht korrekt verarbeitet werden; umständlich, aber anders weiß ichs nich.. quelle.Text:=stringreplace2(quelle.text, '"', 'ANFUEEEE'); quelle.Text:=stringreplace2(quelle.text, #39, 'EINFANFUEEEE'); for ii:=0 to quelle.Count-1 do begin wortarttemp:= gibmirwortart(quelle.strings[ii], #9); //Token zusammennehmen if pos('#' + wortarttemp + '#', tok)<>0 then //Bedingung; braucht keine Zeit, da der zu durchsuchende String tok nur ~40 Zeichen groß ist ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); //Lemma zusammennehmen if pos('#' + wortarttemp + '#', lem)<>0 then //s.o. begin lemmareal:= gibmirlemma(quelle.strings[ii], #9); if (lemmareal= '<UNKNOWN>') and (checkbox2.checked) then //weitere Bedingungen begin ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); end else if (lemmareal= '@card@') and (checkbox4.checked) then begin ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); end else if (lemmareal= 'CARD') and (checkbox4.checked) then begin ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); end else if (lemmareal= '@ord@') and (checkbox4.checked) then begin ziel.text:= ziel.text + gibmirtoken(quelle.strings[ii], #9); end; else ziel.text:= ziel.text + lemmareal; end; //Wortart zusammennehmen if pos('#' + wortarttemp + '#', poss)<>0 then //s.o. ziel.Text:= ziel.text + gibmirwortart(quelle.strings[ii], #9); end; //Wenn die Stringlist quelle durchgearbeitet ist und aller relevanten Strings in ziel, dann sollen die Strings in ziel zu einem fortlaufenden Text (-> zielende: string;) zusammengesetzt werden for x:=0 to ziel.Count-1 do zielende:= zielende + ' ' + ziel.Strings[x]; //Vorherige Ersetzungen rückgängig machen zielende:= stringreplace2(zielende, 'ANFUEEEE', '"'); zielende:= stringreplace2(zielende, 'EINFANFUEEEE', #39); //Ergebnis (Preview) in Memo ausgeben memo2.text:= zielende; finally quelle.free; ziel.Free; end; |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo friedemann,
ich kenne deine Eingabedaten nicht, aber ich vermute, dass die meiste Zeit nicht für das Lesen der Quelldaten, sondern für den Zusammenbau der Ergebnisdaten benötigt wird. Vergleiche einmal die beiden folgenden Code-Blöcke hinsichtlich ihrer Laufzeit:
Delphi-Quellcode:
Bei jedem Lesezugriff auf die Eigenschaft Text einer Stringliste werden alle Strings der Liste verkettet, damit das Ergebnis geliefert werden kann. Bei einem Schreibzugriff auf die Eigenschaft muss der zugewiesene String wieder in die einzelnen Zeilen zerlegt werden. Die Verwendung eines Hilfsstrings führt hier zu einer spürbaren Verkürzung der Laufzeit.
var
T1, T2: Cardinal; SL: TStrings; i: Integer; s: string; begin // Test 1 SL := TStringList.Create; try T1 := GetTickCount; for i := 1 to 20000 do SL.Text := SL.Text + 'Testzeile'; T2 := GetTickCount; ShowMessage (Format('%d Zeilen, %d msec', [SL.Count, T2 - T1])); finally SL.Free; end; // Test 2 SL := TStringList.Create; try T1 := GetTickCount; s := ''; for i := 1 to 20000 do s := s + 'Testzeile' + sLineBreak; SL.Text := s; T2 := GetTickCount; ShowMessage (Format('%d Zeilen, %d msec', [SL.Count, T2 - T1])); finally SL.Free; end; end; Warum nimmst du eigentlich den Umweg über eine Stringliste? Am Ende fügst du ja doch wieder alles zu einem einzelnen String zusammen. Gruß Hawkeye |
Re: TStringlist mit 60000 Einträgen zu langsam
Morgen zusammen,
also ein bisschen hat es was gebracht, aber nicht viel.. Bisschen musste ich anpassen, aber sonst einleuchtend schneller:
Delphi-Quellcode:
Ich versuche jetzt noch das mit FastMM.
function gibmirToken(s:string; sep:char) :string;
begin Result := Copy(s, 1, Pos(#9, s) - 1); end; //hier wird die zweite Stelle des durch tab geteilten Strings aus quelle ermittelt function gibmirwortart(s:string; sep:char) :string; begin Delete(s, 1, Pos(#9, s)); Result := Copy(s, 1, Pos(#9, s) - 1); end; //hier wird die dritte Stelle des durch tab geteilten Strings aus quelle ermittelt function gibmirLemma(s:string; sep:char) :string; begin Delete(s, 1, Pos(#9, s)); Delete(s, 1, Pos(#9, s)); result:= s; end; Danke allen für die Tipps! Grüße, friedemann |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Delphi-Quellcode:
einfach nur
ziel.Text := ziel.Text + ...;
Delphi-Quellcode:
machen können.
ziel.Add(...);
Beim Auslesen dann
Delphi-Quellcode:
PS:
ziel.LineBreak := ' ';
zielende := ziel.Text;
Delphi-Quellcode:
Diese hatte doch eh nichts gebracht.
for x:=0 to ziel.Count-1 do
zielende:= zielende + ' ' + ziel.Strings[x]; Da "ziel" nur aus EINEM langen String besteht oder hab ich irgendwie übersehn, wo mehrere Strings/Zeilen erzeugt werden? Da es aber .LineBreak bei dir noch nicht geben wird,
Delphi-Quellcode:
PS: Da StringRepleace in Delphi nicht grade optimal arbeitet, wäre es besser, wenn du diese mit in die Schleife reinmachst, anstatt es über den GROßEN String zu jagen.
ziel.Add(...);
// und am Ende dann dein for x:=0 to ziel.Count-1 do zielende:= zielende + ' ' + ziel.Strings[x];
Delphi-Quellcode:
for ii:=0 to quelle.Count-1 do
begin ... ziel.Add({die Wörter}); ... end; for x:=0 to ziel.Count-1 do zielende:= zielende + ' ' + stringreplace2(stringreplace2( ziel.Strings[x], 'ANFUEEEE', '"'), 'EINFANFUEEEE', #39); memo2.text := zielende; |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo,
Zitat:
Warum findest du nicht erst mal raus, was so bremst ? Entweder durch einen Profiler oder "auskommentieren". Ich würde z.B. erst mal das ganze "Wort suchen" durch "kompletten String nehmen" ersetzen und testen. #Update#
Delphi-Quellcode:
Wieso muss hier eine StringList erzeugt werden ?
function gibmirLemma(s:string; sep:char) :string;
var t: Tstringlist; begin //hier muss jetzt das zweite Wort rausgefiltert werden t:= tstringlist.create; try extractstrings([char(sep)], [' '], pchar(s), t); result:= t.Strings[2]; finally t.free; end; end; OK, das ominöse ;) extractstrings will das haben. Wie sieht denn der Code extractstrings aus ? [char(sep)] ein offenes Array mit nur einem Zeichen, wozu dann ein array ? Wie es aussiehtst, missbrauchst du die StringList hier, du gibst sie ja auch wieder frei. Heiko |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
|
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo,
ja oder so. Ist hier aber überhaupt nicht notwendig, nur langsamer. Heiko |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo,
ein Text mit TAB-separierten Daten ist doch ein klassischer Fall für einfach von Anfang bis Ende durchparsen - von der Logik her ist es völlig unnötig mehr als eine aktuelle Zeile überhaupt im Speicher zu haben. Wieso muss man hier in der DP immer für einfachste Aufgaben möglichst komplizierte VCL-Elemente benutzen? Auf Ruhm und Ehre unter fanatischen Code-Ideologen kann ich gern verzichten, dafür habe ich dann einfache und schnelle Programme. Einen durchgehenden Text seriell zu verarbeiten ist keineswegs steinzeitlich und hinterwäldlerisch, das machen auch professionelle gekaufte Programme so. Meinetwegen kann jetzt die Prügelei losgehen, ich werde trotzdem so weiter programmieren, ich muss ja hier nichts davon veröffentlichen. Gruss Reinhard |
Re: TStringlist mit 60000 Einträgen zu langsam
@Reinhart, was meinst du damit?
Gruß, friedemann |
Re: TStringlist mit 60000 Einträgen zu langsam
Also auf die schnelle ist mir aufgefallen, das friedemann
1. lokal ständig neue Objekte anlegt und wieder freigibt, was bei diesen Größenordungen nicht zu vernachlässigen ist 2. die string-Parameter als "call by value" übergibt, was bedeuetet, das jedesmal eine Kopie angelegt und wieder freigegeben wird; "const" würde da sehr helfen Ansonsten stimme ich Reinhard zu, ein ordentlicher Parser ist mit Sicherheit die beste Lösung. |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo,
Zitat:
Das Zusammenbauen der Ziel-"Datei" im Speicher würde ich viell. auch so machen. Dann geht das mit dem "Ganz oder gar nicht Speichern" einfacher (OK, man könnte die "halbe" Datei bei einem Fehler wieder löschen) Das "Ganz oder gar nicht Speichern" war ja jetzt keine Anforderung, könnte aber sein ... ;) Ich denke, das Grundproblem sind die "Hole Wort X" Aufrufe. Ob ich jetzt die Ausgangsdatei zeilenweise bearbeite oder alles in eine StringListe packe, ändert daran nichts (vom Speucher abgesehen). Ich würde deshalb ganz gern mal den ExtractStrings-Code sehen. Gab es hier nicht mal eine Diskussion über Explode ? Bei schlechter Laufzeit sollte eh ein Profiler genommen werden, sonst wird an der falschen Stelle optimiert. Zu const Ist mir noch gar nicht aufgefallen ;) An den TE also aus
Delphi-Quellcode:
wird
function gibmirLemma(s:string; sep:char) :string;
Delphi-Quellcode:
function gibmirLemma(const s:string; sep:char) :string;
Heiko |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Aus
Delphi-Quellcode:
wird kurz und knapp
function test1(const s:string; sep:char) :string;
begin Result := S; end;
Code:
dagegen bläht sich
Main.pas.106: begin
push ebx push esi mov esi,ecx mov ebx,eax Main.pas.107: Result := S; mov eax,esi mov edx,ebx call @UStrAsg Main.pas.108: end; pop esi pop ebx ret
Delphi-Quellcode:
ganz fürchterlich zu
function test2(s:string; sep:char) :string;
begin Result := S; end;
Code:
aus. Man muss das ja nicht alles verstehen, aber bei zeitkritischen Sachen summiert sich das ganz erheblich und man sollte es im Hinterkopf behalten. Ich schreibe inszwischen bei Strings ganz automatische immer const davor.
Main.pas.101: begin
push ebp mov ebp,esp push ecx push ebx mov ebx,ecx mov [ebp-$04],eax mov eax,[ebp-$04] call @UStrAddRef xor eax,eax push ebp push $004a3bcc push dword ptr fs:[eax] mov fs:[eax],esp Main.pas.102: Result := S; mov eax,ebx mov edx,[ebp-$04] call @UStrAsg Main.pas.103: end; xor eax,eax pop edx pop ecx pop ecx mov fs:[eax],edx push $004a3bd3 lea eax,[ebp-$04] call @UStrClr ret jmp @HandleFinally jmp $004a3bc3 pop ebx pop ecx pop ebp ret |
Re: TStringlist mit 60000 Einträgen zu langsam
Delphi-Quellcode:
Jupp, am Besten man gewöhnt sich sowas allgemein an, so ist man vor mehrere unangenehmen überraschungen besser geschützt.
Ich schreibe inszwischen bei Strings ganz automatische immer const davor.
(also einfach bei allem, welches nicht direkt mit Integern, Chars, Booleans und Objekten zu tuen hat) |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo,
wie ihr seht, habe ich die entsprechende Passage schon gelöscht ;) Heiko |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Eine einfache readln - verarbeiten - writeln Sequenz ist zwar viel effektiver, aber verpönt. Zitat:
Gruss Reinhard |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Also eine Art TStringStream, welchen man an alle möglichen Streams ranhängen kann. Vorallem an TFileStream und TMemoryStream. Ja, es gibt einen TStringStream, aber der ist ja wohl ein Witz. Ideen dafür existieren schon länger, aber ich hatte nie einen Grund, dieses mal zu machen. :oops: Ein großes Problem exisitert nämlich, denn mit den alten Pascal-Routinen kann man selbst ab D2009 nur ANSI-Dateien erstellen und auslesen. |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo,
Zitat:
![]() ![]() Gruß Hawkeye |
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Gruss Reinhard |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo zusammen,
ist ja richtig was los hier. wie ja schon mehrmals angemerkt wurde ist
Delphi-Quellcode:
nicht so optimal.
function gibmirLemma(s:string; sep:char) :string;
var t: Tstringlist; begin //hier muss jetzt das zweite Wort rausgefiltert werden t:= tstringlist.create; try extractstrings([char(sep)], [' '], pchar(s), t); result:= t.Strings[2]; finally t.free; end; end; wie wäre denn z.B. so etwas:
Delphi-Quellcode:
@reinhard
function gibmirLemma(s:string; sep:char) :string;
var pp : integer; begin result:=''; //hier muss jetzt das zweite Wort rausgefiltert werden pp:=pos(sep,s); if pp>0 then begin pp:=posex(sep,s,pp+1); if pp>0 then result:= copy(s,pp+1,255); end; end; Auch wenn ich Dir im Prinzip zustimme, TStringlist ist einfach genial, besonders wenn die Verarbeitung ein satz vor zwei Satz zurück, einen löschen läuft. Wenn es wirklich nur darum geht (und das hab ich noch nicht erkannt) einen Satz nach dem anderen zu lesen, zu filetieren und dann wegzuschreiben/an einen String anzuhängen würde ich Dich inhaltlich voll unterstützen. und verlier bitte den Unterschied nicht aus den Augen:
Delphi-Quellcode:
var
sl=tstringlist; sl:=tstringlist.create; ... sl.loadfromfile('MeineDaten'); for i:=0 to sl.count-1 do irgendwas sl.free;
Delphi-Quellcode:
Die zweite Möglichkeit ist doch wesentlich aufwendiger.
var
f : textfile; buffer : array [0..8191] of byte; satz : string assign(f,'MeineDaten'); settextbuf(f,buffer,sizeof(buffer)); reset(f); repeat readln(f,satz); machwwasmitsatz unil eof(f); closefile(f); Gruß K-H |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo friedemann2009,
wie viel Speicher verbrauchst Du für die 100.000 Einträge? Welches Windows ist auf dem Rechner installiert? Die geringe Geschwindigkeit könnte auch von Windows verursacht werden, wenn zu viel Speicher belegt wird. Wenn der Speicher voll ist, fängt Windows an Teile davon auf die Festplatte auszulagern. Bis bald Chemiker |
Re: TStringlist mit 60000 Einträgen zu langsam
Hallo,
jetzt geb ich auch mal meinen Senf dazu. Das was hier die meiste Zeit braucht sind doch die vielen gibmir... Aufrufe. Hier wie ich es machen würde:
Delphi-Quellcode:
Damit wird nur 1 mal geparst und dann auf die Teile direkt zugegriffen.
begin
quelle := tstringlist.create; ziel := tstringlist.create; eineZeile := tstringlist.create; try //Previewdatei laden quelle.LoadFromFile(extractfilepath(application.exename) + 'preview2.dat'); eineZeile.Delimiter := #9; eineZeile.QuoteChar := '"'; // hier bin ich mir nicht sicher ob das reicht um die stringreplace überflüssig zu machen for i := 0 to quelle.Count - 1 do begin // vorsichtshalber säubern, muß aber nicht sein eineZeile.Clear; // nur 1 mal parsen (und das von Delphi selbst) statt x mal immerwieder // hier kann wahlweise auch mit readln gearbeitet werden wenn man will eineZeile.DelimitedText := quelle.Strings[i]; //Token zusammennehmen if pos('#' + eineZeile[1] + '#', tok) <> 0 then ziel.text:= ziel.text + eineZeile[0]; //Lemma zusammennehmen if pos('#' + eineZeile[1] + '#', lem) <> 0 then begin schon := 0; if eineZeile[2] in ['<UNKNOWN>', '@card@', 'CARD', '@ord@'] then begin if (eineZeile[2] = '<UNKNOWN>') then begin if checkbox2.checked then begin ziel.text := ziel.text + eineZeile[0]; schon := 1; end; end else // alle anderen begin if checkbox4.checked then begin ziel.text := ziel.text + eineZeile[0]; schon := 1; end; end; end; if schon = 0 then ziel.text := ziel.text + eineZeile[2]; end; if pos('#' + eineZeile[1] + '#', poss) <> 0 then ziel.Text := ziel.text + eineZeile[1]; end; for i := 0 to ziel.Count - 1 do zielende := zielende + ' ' + ziel.Strings[i]; memo2.text := zielende; finally quelle.Free; ziel.Free; eineZeile.Free; end; Ob das jetzt 100 pro funktioniert kann ich aber nicht so aus dem Hut sagen. Um zur Diskusion beizutragen: soweit ich mich erinnern kann nutzt LoadFromFile u.Ä. die Windows API für das eigentliche Ansprechen der Datei, während readln eine Delphi (besser Pascal) eigene Umsetzung der gleichen Funktion ist. An dieser Stelle lässt sich darüber streiten, ob die Windows eigene oder die Borland/CodeGear/Embarcadero umsetzung der selben schneller ist. Ich bin ja (auch wenn ich das sonst nicht so sehe) eher für MS, alleine durch die Tatsache das MS die Funktionen auf die jeweilige Windows Version anpassen kann und Delphi von der Plattform wo das Programm dann läuft zur Compiletime keine Ahnung haben kann. Bis Dann, und hoffentlich konnte ich helfen, David Nachtrag: Ich hab nochmal zusammegerechnet, du zerpflückst jede Zeile 5 mal, da hast du dein Performanceproblem. Wenn es so wie in meinem Code nicht geht kannst du auch eine Funktion schreiben die alle 3 Teile auf einmal zurückgibt und dann im "Hauptprogramm" in 3 einzelne Variablen speichert. So in etwa:
Delphi-Quellcode:
procedure aufteilen(const s : string; const sep : char; var Token, wortart, Lemma : string);
var t: Tstringlist; begin //hier muss jetzt das zweite Wort rausgefiltert werden t:= tstringlist.create; try extractstrings([char(sep)], [' '], pchar(s), t); Token := t.Strings[0]; wortart := t.Strings[1]; Lemma := t.Strings[2]; finally t.free; end; end; // so wirds dann benutzt, einmal am Anfang aufteilen(quelle.strings[ii], #9, token_Variable, wortarttemp, lemmareal); |
Re: TStringlist mit 60000 Einträgen zu langsam
Eventuell tut es in diesem speziellen Fall auch schon
Delphi-Quellcode:
?
meineStringliste.sorted := true;
|
Re: TStringlist mit 60000 Einträgen zu langsam
Die Sortierung ist, glaube ich, schon wichtig. Mit .sorted := true; geht die verloren.
|
Re: TStringlist mit 60000 Einträgen zu langsam
Zitat:
Danke und Gruß, friedemann NACHTRAG: - Falls ich es noch nicht schrieb: Thanks a lot für die vielen Hinweise! |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06: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-2025 by Thomas Breitkreuz