AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Geschwindigkeit einer Rekursion unter D2010 erhöhen
Thema durchsuchen
Ansicht
Themen-Optionen

Geschwindigkeit einer Rekursion unter D2010 erhöhen

Offene Frage von "kaju74"
Ein Thema von kaju74 · begonnen am 10. Mär 2010 · letzter Beitrag vom 10. Mär 2010
Antwort Antwort
Seite 1 von 2  1 2      
kaju74

Registriert seit: 22. Okt 2003
185 Beiträge
 
#1

Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 08:37
Hallo.

Ich habe hier ein kleines Verständnisproblem, warum folgender Code extrem langsam wird, wenn es viele (XML) Unterelemente gibt:

Delphi-Quellcode:
function TXmlDocument.Content: string;

const
  CrLf = #13#10;
  Idnt = #32#32;

{ Expand }

  function Expand(const Str, Indent: string; const Elements: TXmlElements): string;
  var
    i: Integer;
    Content: string;
    SmartEnd: Boolean;
  begin
    Result := Str;
    for i := 0 to Elements.Count - 1 do
    begin
      Result := Result + Indent + '<' + Elements[i].Name;
      Content := Trim(Elements[i].Content);
      SmartEnd := not((Content <> '') or (Elements[i].Elements.Count > 0));
      if not(SmartEnd) then
        Result := Result + '>' + Content;
      if Elements[i].Elements.Count > 0 then
        Result := Expand(Result + CrLf, Indent + Idnt, Elements[i].Elements) + Indent;
      if not(SmartEnd) then
        Result := Result + Format('</%s>', [Elements[i].Name])
      else
        Result := Result + '/>';
      Result := Result + CrLf;
    end;
  end;

begin
  Result := '<?xml version="1.0" encoding="utf-8" ?>' + #13#10 + Expand('', '', Elements);
end;
Hierbei handelt es sich um eine kleine Routine, die rekursiv alle Xml-Elemente durchgeht und einen String
mit dem kompletten Inhalt zurückliefert. Bei einem Baum von 100 Knoten mit jeweils 50 Unterknoten braucht die
Routine auf meinem Rechner stolze 18 Sekunden.

Schritt1: String durch AnsiString ersetzen

Da ich derzeit sowie keine Unicode Xml-Dateien unterstütze und brauche, spare ich mir die ganzen Umwandlungen
und ersetze String durch AnsiString:

Delphi-Quellcode:
function TXmlDocument.Content: AnsiString;

const
  CrLf: AnsiString = #13#10;
  Idnt: AnsiString = #32#32;

{ Expand }

  function Expand(const Str, Indent: AnsiString; const Elements: TXmlElements): AnsiString;
  var
    i: Integer;
    Content: AnsiString;
    SmartEnd: Boolean;
  begin
    Result := Str;
    for i := 0 to Elements.Count - 1 do
    begin
      Result := Result + Indent + '<' + AnsiString(Elements[i].Name);
      Content := AnsiString(Trim(Elements[i].Content));
      SmartEnd := not((Content <> '') or (Elements[i].Elements.Count > 0));
      if not(SmartEnd) then
        Result := Result + '>' + Content;
      if Elements[i].Elements.Count > 0 then
        Result := Expand(Result + CrLf, Indent + Idnt, Elements[i].Elements) + Indent;
      if not(SmartEnd) then
        Result := Result + AnsiString(Format('</%s>', [Elements[i].Name]))
      else
        Result := Result + '/>';
      Result := Result + CrLf;
    end;
  end;

begin
  Result := '<?xml version="1.0" encoding="utf-8" ?>' + #13#10 + Expand('', '', Elements);
end;
Somit komme ich von ~18 Sekunden runter auf ~8 Sekunden. Nun habe ich aber folgendes Phänomen, das ich
nicht so ganz erklären kann:

Ersetze ich die beiden Konstanten am Anfang des Codes durch:

Delphi-Quellcode:
const
  CrLf: '';
  Idnt: '';
Werden aus den ~8 Sekunden schlanke 0,75 Sekunden (und aus den urspünglichen 18 Sekunden für Unicode etwa 4!).

WARUM???

Was kann ich tun, um trotzdem CarriageReturn/Linefeeds und Spaces in meiner Ausgabe zu erhalten, ohne das die
Geschwindigkeit wieder zusammenbricht?

Vielen Dank & Gruß,
kaju
  Mit Zitat antworten Zitat
Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#2

Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 08:41
Das hat nichts mit der Rekursion an sich zu tun: Wiederholte String Concatenation (ist das richtig geschrieben?) ist generell nicht besonders schnell. Und wenn man dann noch eine Schleife drumherum baut, wirds auch nicht schneller

Probier mal den StringBuilder aus
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 09:37
Oder eine "ordentliche" XML-Lib nutzen.

Zitat:
TXmlDocument.Content
Wer hat denn dieses überhaupt verbrockt?

PS: Diese Funktion ließe sich auch sehr leicht über eine StringList oder den genannten StringBuilder etwas optimieren.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
kaju74

Registriert seit: 22. Okt 2003
185 Beiträge
 
#4

Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 10:13
Hallo.

Die Lib ist schon älter...will die aber nicht unbedingt überall ersetzen müssen. Ich weiß, dass es wesentlich
bessere Libs wie OmniXML gibt, aber in einem großen, vorhandenen Projekt lässt sich das nicht mal eben schnell
ändern...das mit dem StringBuilder gucke ich mir mal etwas genauer an - Danke!

Lieben Gruß,
kaju
  Mit Zitat antworten Zitat
samso

Registriert seit: 29. Mär 2009
439 Beiträge
 
#5

Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 10:29
Die eigentliche Frage
Zitat:
Was kann ich tun, um trotzdem CarriageReturn/Linefeeds und Spaces in meiner Ausgabe zu erhalten, ohne das die
Geschwindigkeit wieder zusammenbricht?
kann ich leider auch nicht beantworten. Ich finde diese Erscheinung auch erstmal erstaunlich. Ist die Compiler-Option "String-Formatprüfung" eingeschaltet? Wie ist es, wenn Du nur "Idnt: '';" setzt, aber CrLF=#13#10 lässt? Mir fällt eigentlich nur auf, dass der Compiler bei der Sequenz:
Result := Expand(Result + CrLf, Indent + Idnt, Elements[i].Elements) + Indent; mit CrLf='' und Idnt='' keine temporären Strings für "Result + CrLf" und "Indent + Idnt" erstellen muss. Da auch Indent dann leer bleibt, reduziert sich der Aufruf dann in der Konsequenz auf
Result := Expand(Result, '', Elements[i].Elements)); Hier würde ich vielleicht ansetzten. Ich hoffe, ich habe keinen fürchterlichen Denkfehler gemacht, aber geht es auch so???
Delphi-Quellcode:
  procedure Expand(const Indent: string; const Elements: TXmlElements);
  var
    i: Integer;
    Content: string;
    SmartEnd: Boolean;
  begin
    for i := 0 to Elements.Count - 1 do
    begin
      Result := Result + Indent + '<' + Elements[i].Name;
      Content := Trim(Elements[i].Content);
      SmartEnd := not((Content <> '') or (Elements[i].Elements.Count > 0));
      if not(SmartEnd) then
        Result := Result + '>' + Content;
      if Elements[i].Elements.Count > 0 then
      begin
        Result := Result + CrLf;
        Expand(Indent + Idnt, Elements[i].Elements) + Indent;
      end;
      if not(SmartEnd) then
        Result := Result + '</' + Elements[i].Name + '>'
      else
        Result := Result + '/>';
      Result := Result + CrLf;
    end;
  end;
Falls ich mir das korrekt überlegt haben sollte, würde man ziemlich oft das Kopieren des Result-Strings vermeiden.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 11:27
vesuch mal Dieses
Delphi-Quellcode:
function TXmlDocument.Content: string;
var
  SL: TStringList;

  procedure Expand(const Indent: string; Elements: TXmlElements);
  var
    i: Integer;
    S: String;
  begin
    for i := 0 to Elements.Count - 1 do
      if Elements[i].Elements.Count > 0 then begin
        SL.Add(Indent + '<' + Elements[i].Name + '>');
        Expand(Indent + ' ', Elements[i].Elements);
        SL.Add(Indent + '</' + Elements[i].Name + '>');
      end else begin
        S := Trim(Elements[i].Content);
        if S <> 'then
          SL.Add(Indent + '<' + Elements[i].Name + '>' + S + '</' + Elements[i].Name + '>')
        else
          SL.Add(Indent + '<' + Elements[i].Name + '/>');
      end
  end;

begin
  SL := TStringList.Create;
  try
    SL.Add('<?xml version="1.0" encoding="utf-8" ?>');
    Expand('', Elements);
    Result := SL.Text;
  finally
    SL.Free;
  end;
end;
Beim Original war nicht nur die große Laufzeit das Schlimme.
Schlecht war auch die Art der Parameter/Speicherverwaltung.
So wurde bei mehrfach verschalteten Nodes ein Vielfaches des Speichers belegt, welches auch noch mit zunehmender Anzahl der ChildNodes anwuchs, da der String über Str mehrfach verwaltet wurde. (welches samso auch schon gut beseitigt hat)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
kaju74

Registriert seit: 22. Okt 2003
185 Beiträge
 
#7

Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 11:48
Hallo.

Vielen lieben Dank - dieses Forum ist echt gold wert )))) Werde das umgehend mal umsetzen.

Nochmals, vielen Dank.

Gruß,
- kaju
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 11:56
Was mir gerade auffällt, alle diese Codes sollten ab D2009 Probleme in der Codierung bekommen, vorallem wenn du nicht aufpaßt.

String = Unicode
aber der XML-Header sagt es wäre UTF-8

OK, man könnte z.B. den UnicodeString jetzt einfach nach UTF-8 umkodieren und so speichern, aber dann muß der Text in Elements[i].Content auch als Unicode vorliegen und darf kein falsch codiertes UTF-8 enthalten.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
kaju74

Registriert seit: 22. Okt 2003
185 Beiträge
 
#9

Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 12:10
Nochmal Hallo.

Habe das mal eben eingebaut...schon krass...jetzt sind wir bei 0.015 Sekunden !!!! Was ein
Unterschied.

Nochmal Danke...

Wenn ich das richtig verstehe ist der große Unterschied, das bei der alten Lösung auch immer wieder
neuer Speicher allokiert werden musste während die Stringliste nur am Ende durchgeht und den
Resultatstring zusammenbaut.

Oje...machmal glaube ich, ich hab den Beruf verfehlt ))

Lieben Gruß,
- kaju
  Mit Zitat antworten Zitat
kaju74

Registriert seit: 22. Okt 2003
185 Beiträge
 
#10

Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen

  Alt 10. Mär 2010, 13:03
Nochnmal Hallo.

Um nun noch die fehlenden Attribute mit aufzunehmen, wäre das hier die schnellste Möglichkeit, oder macht man das dann wieder anders noch schneller?

Delphi-Quellcode:
function TNXXmlDocument.Content: string;
var
  ElementList: TStringList;
  AttributeList: TStringList;

{ ExpandElements }

  procedure ExpandElements(const Indent: string; Elements: TNXXmlElements);
  var
    Str: String;
    i, j: Integer;
  begin
    for i := 0 to Elements.Count - 1 do
    begin
      AttributeList.Clear;
      for j := 0 to Elements[i].Attributes.Count - 1 do
        AttributeList.Add(Elements[i].Attributes[j].Name + '="' + Elements[i].Attributes[j].Value + '"');
      if Elements[i].Elements.Count > 0 then
      begin
        ElementList.Add(Indent + '<' + Trim(Elements[i].Name + ' ' + AttributeList.DelimitedText) + '>');
        ExpandElements(Indent + ' ', Elements[i].Elements);
        ElementList.Add(Indent + '</' + Elements[i].Name + '>');
      end
      else
      begin
        Str := Trim(Elements[i].Content);
        if Str <> 'then
          ElementList.Add(Indent + '<' + Trim(Elements[i].Name + ' ' + AttributeList.Text) + '>' + Str + '</' + Elements[i].Name + '>')
        else
          ElementList.Add(Indent + '<' + Trim(Elements[i].Name + ' ' + AttributeList.Text) + '/>');
      end
    end;
  end;

begin
  ElementList := TStringList.Create;
  try
    ElementList.Add('<?xml version="1.0" encoding="utf-8" ?>');
    AttributeList := TStringList.Create;
    try
      AttributeList.QuoteChar := #0;
      AttributeList.Delimiter := ' ';
      ExpandElements('', Elements);
      Result := ElementList.Text;
    finally
      AttributeList.Free;
    end;
  finally
    ElementList.Free;
  end;
end;
Danke & Gruß,
- kaju
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 15:59 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