AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

TStringlist mit 60000 Einträgen zu langsam

Ein Thema von friedemann2009 · begonnen am 10. Feb 2010 · letzter Beitrag vom 12. Feb 2010
Antwort Antwort
Seite 2 von 5     12 34     Letzte »    
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.171 Beiträge
 
Delphi 12 Athens
 
#11

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 10. Feb 2010, 14:31
Zitat von hoika:
Die 1 GB RAM könnten schon das Problem sein,
wenn Delphi auch noch offen ist.
So hungrig war Delphi 5 noch nicht.

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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von p80286
p80286

Registriert seit: 28. Apr 2008
Ort: Stolberg (Rhl)
6.659 Beiträge
 
FreePascal / Lazarus
 
#12

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 10. Feb 2010, 15:28
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
Programme gehorchen nicht Deinen Absichten sondern Deinen Anweisungen
R.E.D retired error detector
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#13

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 10. Feb 2010, 16:23
Zitat von Tyrael Y.:
...da Stringoperationen nicht gerade sehr schnell sind...würde ich sagen, daß das Benutzen der StringListe selbt auch schon ein Fehler ist...
Wieso? Ich habe 100.000 Strings in einer Stringlist. Jedes Element mit 'Copy' zu bearbeiten, dauert 30ms, jeweils einmal mit POS drüber dauert 10-15ms.. Sonderlich langsam ist das nicht, oder?
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.
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#14

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 10. Feb 2010, 16:56
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.
Andreas
  Mit Zitat antworten Zitat
friedemann2009

Registriert seit: 10. Feb 2010
49 Beiträge
 
#15

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 10. Feb 2010, 21:38
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
  Mit Zitat antworten Zitat
Benutzerbild von cookie22
cookie22

Registriert seit: 28. Jun 2006
Ort: Düsseldorf
936 Beiträge
 
Delphi XE2 Professional
 
#16

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 10. Feb 2010, 22:17
FastMM gibs hier:http://sourceforge.net/projects/fastmm/

binde es einfach mal ins projekt ein und schau obs schneller geht.

und schau dir mal die faststrings.pas an: http://cc.embarcadero.com/Item/18628
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.171 Beiträge
 
Delphi 12 Athens
 
#17

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 10. Feb 2010, 22:19
Delphi-Quellcode:
//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;
Versuch es erstmal hiermit.

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;
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Hawkeye219

Registriert seit: 18. Feb 2006
Ort: Stolberg
2.227 Beiträge
 
Delphi 2010 Professional
 
#18

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 10. Feb 2010, 23:06
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:
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;
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.

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
  Mit Zitat antworten Zitat
friedemann2009

Registriert seit: 10. Feb 2010
49 Beiträge
 
#19

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 11. Feb 2010, 07:10
Morgen zusammen,

also ein bisschen hat es was gebracht, aber nicht viel..

Bisschen musste ich anpassen, aber sonst einleuchtend schneller:
Delphi-Quellcode:
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;
Ich versuche jetzt noch das mit FastMM.

Danke allen für die Tipps!
Grüße,
friedemann
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.171 Beiträge
 
Delphi 12 Athens
 
#20

Re: TStringlist mit 60000 Einträgen zu langsam

  Alt 11. Feb 2010, 07:20
Zitat von Hawkeye219:
aber ich vermute, dass die meiste Zeit nicht für das Lesen der Quelldaten, sondern für den Zusammenbau der Ergebnisdaten benötigt wird
...
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.
In neueren Delphiversionen hätte man auch einfach statt
ziel.Text := ziel.Text + ...; einfach nur
ziel.Add(...); machen können.

Beim Auslesen dann
Delphi-Quellcode:
ziel.LineBreak := ' ';
zielende := ziel.Text;
PS:
Delphi-Quellcode:
for x:=0 to ziel.Count-1 do
  zielende:= zielende + ' ' + ziel.Strings[x];
Diese hatte doch eh nichts gebracht.
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:
ziel.Add(...);

// und am Ende dann dein
for x:=0 to ziel.Count-1 do
  zielende:= zielende + ' ' + ziel.Strings[x];
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.

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;
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 5     12 34     Letzte »    


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:51 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz