![]() |
Geschwindigkeit einer Rekursion unter D2010 erhöhen
Hallo.
Ich habe hier ein kleines Verständnisproblem, warum folgender Code extrem langsam wird, wenn es viele (XML) Unterelemente gibt:
Delphi-Quellcode:
Hierbei handelt es sich um eine kleine Routine, die rekursiv alle Xml-Elemente durchgeht und einen String
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; 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:
Somit komme ich von ~18 Sekunden runter auf ~8 Sekunden. Nun habe ich aber folgendes Phänomen, das ich
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; nicht so ganz erklären kann: Ersetze ich die beiden Konstanten am Anfang des Codes durch:
Delphi-Quellcode:
Werden aus den ~8 Sekunden schlanke 0,75 Sekunden (und aus den urspünglichen 18 Sekunden für Unicode etwa 4!).
const
CrLf: ''; Idnt: ''; 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 |
Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen
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 ;) |
Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen
Oder eine "ordentliche" XML-Lib nutzen.
Zitat:
PS: Diese Funktion ließe sich auch sehr leicht über eine StringList oder den genannten StringBuilder etwas optimieren. |
Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen
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 |
Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen
Die eigentliche Frage
Zitat:
Delphi-Quellcode:
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 + CrLf, Indent + Idnt, Elements[i].Elements) + Indent;
Delphi-Quellcode:
Hier würde ich vielleicht ansetzten. Ich hoffe, ich habe keinen fürchterlichen Denkfehler gemacht, aber geht es auch so???
Result := Expand(Result, '', Elements[i].Elements));
Delphi-Quellcode:
Falls ich mir das korrekt überlegt haben sollte, würde man ziemlich oft das Kopieren des Result-Strings vermeiden.
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; |
Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen
vesuch mal Dieses
Delphi-Quellcode:
Beim Original war nicht nur die große Laufzeit das Schlimme.
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; 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) |
Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen
Hallo.
Vielen lieben Dank - dieses Forum ist echt gold wert 8-))))) Werde das umgehend mal umsetzen. Nochmals, vielen Dank. Gruß, - kaju |
Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen
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. |
Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen
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 8-))) Lieben Gruß, - kaju |
Re: Geschwindigkeit einer Rekursion unter D2010 erhöhen
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:
Danke & Gruß,
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; - kaju |
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:57 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