![]() |
[Optimiert] Explode Prozedur - Reloaded (Ersatz für CodeLib)
Liste der Anhänge anzeigen (Anzahl: 1)
Hi!
Ich hab mir mal die Mühe gemacht, und eine alternative Explode-Funktion implementiert, die wohl doch etwas schneller ist, als die hier in der ![]() Ich möchte Euch bitten, den Code zu testen und auch zu optimieren. Wenn wir damit durch sind, sollte diese Version in die Code-Library übernommen werden, bzw die jetzige Version ersetzen. Details über die Herleitung (na ja, Recherche und Kopieren :mrgreen: ) steht im Code. [Edit] Unten genannte Tests sowie Zeitmessung eingearbeitet: Es ist ein komplettes Projekt mit Funktions- und Speed-Test. Bitte versucht, Teile davon zu optimieren (ASM, Pointer arithmetic etc.). [/edit] History: 12.12. Version 1.1: Erste schwere Fehler ausgebaut: Strings am Ende wurden falsch bzw. gar nicht erkannt. 13.12. Version 1.2: Kleiner Fehler in der Prepare-Methode: (Hilfsvariable als Cardinal deklariert, bei Zuweisung <0 ignoriert), Facelifting auf Anregung von Elvis (Feld- und Variablennomenklatur). Ferner ist eine Test-Iterator-Basisklasse sowie ein Iterator für Char-Delimiter (z.B. für CSV) implementiert. 14.12 Version 1.3: Iterator für QS-Search implementier. Die Test-Routine wurde um Zeitmessungen für den Iterator sowie die Code-Library ergänzt. 23.12.07 Version 1.4: Beseitigt seltenen Bereichsüberlauf am Ende eines Strings. Diese Version ist zwischen 4 und 1000x schneller als die Version aus der Code-Library. |
Re: [Bitte optimieren] Explode Prozedur - Reloaded
Danke für den Code. Ist doch um einiges schneller als eine herkömmliche Explode Funktion.
Habe es getestet mit einigen einigen tausend Aufrufen der jeweiligen Funktion und mit Messen der Zeit. |
Re: [Bitte optimieren] Explode Prozedur - Reloaded
Interessant!! :dp:
Gibt es auch einen Unittest, um die Funktionsfähigkeit zu weisen? Gerade bei so einer komplexen Funktion sollte man diese Tests unbedingt ausführen und bei jeder weiteren Optimierung erneut ausführen.
Delphi-Quellcode:
PS: Vielleicht sollte man den Boyer-Moore Algorithmus in eine eigene Klasse verlagern,
// ungetestet reingehackt
procedure TestTStringDivider; var sd : TStringDivider; list : TStringList; s, delim : string; begin sd := TStringDivider.Create; list := TStringList.Create; delim := 'abcd'; // Test #1 s := ''; sd.Explode(s, delim, list); Assert(list.Count=0); // Test #2 s := 'delphi'+delim+'Praxis'; sd.Explode(s, delim, list); Assert(list.Count=2); Assert(list[0] = 'delphi'); Assert(list[0] = 'Praxis'); // Test #3 s := 'delphi'+delim+'Praxis'+delim+delim+delim; sd.Explode(s, delim, list); Assert(list.Count=5); ... end; um so einen zusätzlichen Nutzen zu gewinnen. |
Re: [Bitte optimieren] Explode Prozedur - Reloaded
Hallo!
Danke für das Feedback, Ich habe wirklich nur halbe Arbeit geleistet. Die Test-Unit ist natürlich klasse, auch eine standardisierte Zeitmessung sollte man in ein Testszenario aufnehmen. Den Suchalgorithmus kann man natürlich auslagern, aber ich meine, man könnte einfach zu ![]() Ich arbeite gleich mal die beiden Vorschläge ein. |
Re: [Bitte optimieren] Explode Prozedur - Reloaded
Liste der Anhänge anzeigen (Anzahl: 2)
Bug gefunden, Bug gefunden, Trallalla *drei-mal im Kreis hüpft* :mrgreen:
Ich habe mal die Unit etwas angepasst, da sie im Single Char modus gerne das letzte Zeichen geklaut hat. Ich habe auch ctrl+shift+e benutzt um die etwas kurzen Name hoffentlich klarer zu machen. Außerdem habe ich den Code mal in externe Iteratoren geworfen, für den Fall dass man die Ergebnisse a) nicht in einer Liste halten möchte und b) die VCL nicht referenzieren will. Gibt ja leider keinen String Container in der Delphi RTL. (RTL <> VCL) Und c) weil's so easy war :mrgreen:
Den Fehler und die Verwendung der Iteratoren kann man hiermit sehen:
Delphi-Quellcode:
btw: Warum hast du hier Instanzmethoden gewählt obwohl du gar keinen State zwichen den Calls halten musst?
uses
Classes, csExplode2, uExplodeEnumerators, csExplode; type TOriginalStringDivider = csExplode.TStringDivider; TStringDivider = csExplode2.TStringDivider; procedure Original(const aPattern, aText : String); var s : string; sl : TStringList; sd : TOriginalStringDivider; begin sl := TStringList.Create(); sd := TOriginalStringDivider.Create(); sd.Explode(aPattern, aText, sl); for s in sl do Writeln(s); sd.Free(); sl.Free(); end; procedure UseSL(const aPattern, aText : String); var s : string; sl : TStringList; begin sl := TStringList.Create(); TStringDivider.Explode(aPattern, aText, sl); for s in sl do Writeln(s); sl.Free(); end; procedure UseEnum(const aPattern, aText : String); var s : string; begin for s in TStringDivider.Explode(aPattern, aText) do Writeln(s); end; procedure UseEnumDirectly(const aPattern, aText : String); var enum : IExplodeEnumerator; begin enum := TStringDivider.Explode(aPattern, aText) as IExplodeEnumerator; while enum.MoveNext() do Writeln(enum.Current); end; procedure RunAll(const aPattern, aText : String); begin Original(aPattern, aText); Writeln('----------------------------'); UseSL(aPattern, aText); Writeln('----------------------------'); UseEnum(aPattern, aText); Writeln('----------------------------'); UseEnumDirectly(aPattern, aText); Writeln('----------------------------'); end; begin ReportMemoryLeaksOnShutdown := true; RunAll('abcxydefxyghixymmmxyx','y'); RunAll('abcxydefxyghixymmmxyy','y'); end. Klassenmethoden hätten ja auch gereicht, bzw. sogar statische methoden in Delphi2006, wodurch du dir den impliziten parameter auf die class reference sparst:
Delphi-Quellcode:
type
TStringDivider = class private class procedure AddString(pStart, pEnd: PChar; aItems: TStrings); static; class procedure QSExplode(const aText, aPattern: String; aItems: TStrings); static; public class procedure Explode(const aText, aPattern: String; aItems: TStrings); overload; static; //class function Explode(const aText, aPattern: String) : IExplodeEnumerable; overload; static; end; btw: Wer in den unteren Procs (UseEnum*) nach Free sucht, sucht vergebens, da ich mit Interfaces arbeite überlasse ich das dem Compiler und der Referenzzählung. ;) |
Re: [Bitte optimieren] Explode Prozedur - Reloaded
Hi Elvis,
In der Test-Dpr dürften die Fehler behoben gewesen sein, nur in der isolierten csExplode.Pas-Unit waren sie es nicht. Ich schmeiss das Attachment mal raus. Danke für das Bugfixing. Zu deinen Anmerkungen: Zitat:
Zitat:
Zitat:
Zitat:
Sollte sich Boyer-Moore (oder ein anderes Verfahren) doch als schneller erweisen, wird die Berechnung der Sprungtabelle(n) noch aufwändiger. Insofern ist es vorteilhaft, wenn nicht sogar zwingend, diese Berechung auszulagern und ggf. nur einmalig aufzurufen. |
Re: [Bitte optimieren] Explode Prozedur - Reloaded
Zitat:
Zitat:
Einfach das "if" rausschmeißen und gut ist. Zitat:
Zitat:
Die Viecher lassen Code ziemlich schnell ziemlich ekelerregend aussehen, IMHO. ;) Ausnahme sind open const arrays, die [1,2,3,] als Parameter ermöglichen. Zitat:
Zitat:
|
Re: [Bitte optimieren] Explode Prozedur - Reloaded
Wie kann ich die Funktion Explode ändern, so dass aItems vom Typ "array of string" ist?
Delphi-Quellcode:
also z.B
Procedure Explode(Const aText, aPattern: String; aItems: TStrings);
Delphi-Quellcode:
type
TStrArray = array of string; Procedure Explode(Const aText, aPattern: String; aItems: TStrArray); |
Re: [Bitte optimieren] Explode Prozedur - Reloaded
Hallo API, das kann man ohne Probleme einbauen. Ich wollte jedoch zunächst die notwendigen Optimierungen durchführen, eventuell den Quick-Search durch einen noch schnelleren ersetzen sowie die 'One-Char-Delimiter' Variante als ASM o.ä. implementieren (lassen).
Dann kann man eine zweite Klasse basteln, die Anstelle eines TStrings ein String-Array befüllt, oder einen Iterator, wie Elvis in beschreibt. Zwei grundsätzliche Dinge: Ein Iterator ist zwischen 1% und 30% langsamer, je nach Länge des zu suchenden Textes und Anzahl der Teilstrings. Da dieser Thread der Performanceoptimierung der Explode-Funktion dient, möchte ich den Iterator erst am Ende basteln. Grundsätzlich würde ich eine Implementierung der Explodefunktion auf Basis eines Iterators natürlich viel eleganter finden, aber die 30% Einbuße nehme ich nicht in Kauf, bloß um OO-konform zu sein. Ich verstehe den Unterschied zwischen dynamischen Stringarrays und TStrings nicht (jedenfalls in diesem Fall): Beide müssen dynamisch angepasst werden und der Overhead einer TStringList ggü dem TStringArray dürfte minimal sein. Trotzdem könnte man eine TString-Array-Variante implementieren. Lasst uns doch einfach weiter so an dem Teil basteln, das eine wirklich optimale Lösung entsteht. Wir können dann alle hier diskutierten Varianten (Iterator, TStringArray etc.) abschließend in die Code-Library packen. Elvis Ansatz des Iterators ist schon ziemlich elegant. Er hatte nur nicht die richtige Basisklasse, ansosnten wäre das Teil schon perfekt. :thumb: [edit] Ich hab eben eine Boyer-Moore-Variante getestet, das derzeit beste Stringmatching-Verfahren... :gruebel: Kann ja sein, aber hier ist es ca. 40% langsamer :cry: [/edit] |
Re: [Bitte optimieren] Explode Prozedur - Reloaded
Zitat:
Zitat:
Zitat:
Wenn man die Dinge, die ihn wirklich kosten lassen, (Der Fieldoffset von zu vielen einzelnen Variablen an zu vielen Stellen zum Beispiel) minimiert, denke ich dass er nur 5-7% hinter einer wirklich krank optimierten Variante hängen wird. Solange er natürlich ähnlich stark optimiert ist. Interessant wird sowas, wenn man möglichst wenig Speicher auf einmal reservieren will. Wenn der Input zum Beispiel ein Stream ist, der sich durch eine Datei bewegt und wirklich nur die gefundenen Schnipsel ausgespuckt werden sollen. Das ist es zumindest wofür ich solche Iteratoren in meinen (Chrome/.Net) Programmen benutze. Wobei es ziemlich friemelig ist sowas selbst zu bastelt, im Gegensatz zum Chrome compiler, der dir autom. einen optimierten Iterator aus deinem Code generieren kann... Zitat:
Zitat:
ediT: hui, da waren auch wieder ein paar Fipptehler drin :shock: |
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 by Thomas Breitkreuz