Autor |
Beitrag |
2cHH
      
Beiträge: 46
|
Verfasst: So 27.08.06 18:53
Hi,
mittlerweile habe ich mich mal versucht etwas mit der IDE vertraut zu machen. Weil ich sowieso schon länger vorhatte, ein einfaches Programm zu schreiben, mit dem man mehrzeilige Textersetzungen vornehmen kann, ohne sich erst mit RegEx oder einem Macrorecorder beschäftigen zu müssen, ist das dabei herausgekommen:
[url= img135.imageshack.us...35/9097/1apptr9.png] [/URL]
Funktioniert auch, geht aber bei Texten mit über 50.000 Zeichen zu sehr in die Knie. Wie kann ich das Programm (oder besser die eine Zeile  ) schneller machen?
Delphi-Quelltext 1: 2: 3:
| RichEditSource.Text := AnsiReplaceStr(RichEditSource.Text, RichEditSearch.Text, RichEditReplace.Text); |
Das hier habe ich schon versucht, macht aber scheinbar keinen allzugrossen Unterschied:
Delphi-Quelltext 1: 2: 3: 4:
| cont := RichEditSource.Text; RichEditSource.Text := AnsiReplaceStr(cont, RichEditSearch.Text, RichEditReplace.Text); |
Welche Möglichkeiten gibt es, um die Performance zu steigern? Moderiert von Tino: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Mo 28.08.2006 um 08:39
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: So 27.08.06 19:27
Das ist ein Systematischer Fehler in der VCL, da Du gleich zwei Flaschenhälse in der String-Replace-Routne drin hast.
Das Beste für Deinen Fall wäre, die StringReplace-Funktion komplett selber zu schreiben, was gar nicht so schwer ist.
Ich knn leider den Quelltext der beiden Routinen in der VCL nicht als Referenz die Flaschenhälse aufzeigen, da ich den auf nem anderen Rechner hab, grob soviel zu den Fehlern: Die VCL alloziiert Speicher in Schleifen, immer wenn eine neue Ersetzung vorgenommen wurde ... Ferner werden jedes mal 3 Kopien der Strings gehandhabt, die vollständig umkopiert werden müssen, was aber vollkommener Overhead ist ...
Eine optimierte Version sieht folgendermaßen aus:
- Berechne den maximal anfallenden Speicher, wenn das Maximum an Ersetzungen vorgenommen werden muss vs. Stringlänge ohne Ersetzung und nehme den größeren Wert (MaxReplaceSize := Max((OrigStringLen div OrigNeedleSize) * ReplacedStringLen + OrigStringLen mod OrigNeedleSize, OrigStringLen);)
- Reserviere einen String entsprechender Größe (SetLength(Result, MaxReplaceSize);)
- Durchsuche den OriginalString zeichenweise nach dem Suchmusterr:
- Wenn das Suchmuster nicht mit dem aktuellen Zeichen übereinstimmt, wird das aktuelle Zeichen in den Ausgabestring kopiert.
- Stimmt das erste Zeichen mit dem des Sucmusters überein, merkt man sich die aktuelle Position und prüft auf Übereinstimmung
- Bei Übereinstimmung schreibt man den Ersetzungsstring in die Ausgabe
- Bei nichtübereinstimmung kopiert man soviele Zeichen in die Ausgabe, wie man zur Feststellung der Abweichung vom Suchmuster gebraucht hat
- Anpassen des Result-Strings mit SetLength um überflüssige Zeichen abzuschneiden (SetLength(Reesult, ActualSize);).
Ich denk, die Frage ist aber in ASM\Opti besser aufgehoben ...
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: So 27.08.06 20:14
BenBE hat folgendes geschrieben: | Das ist ein Systematischer Fehler in der VCL |
Du meinst wohl RTL und nicht VCL. Und auch in der RTL ist es "nur" der Speichermanager, der ab Delphi 2006 durch einen neuen ersetzt wurde, der dieses Verhalten nicht mehr so extrem auslebt.
Zitat: | Die VCL alloziiert Speicher in Schleifen |
Hier: "VCL" durch "AnsiStringReplace" ersetzen.
(die VCL ist nicht für alles der Schuldige  )
_________________ Ist Zeit wirklich Geld?
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: So 27.08.06 20:25
AnsiReplaceStr ist also der Schludige, thnx.
Zuletzt bearbeitet von 2cHH am So 27.08.06 20:26, insgesamt 1-mal bearbeitet
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mo 28.08.06 11:08
Hallo,
hier : fastcode.sourceforge.net/
gibt fuer viele Funktionen eine zum erhebliche schnellere Ersetzung.
Insbesondere ansistringreplace fastcode.sourceforge...siStringReplace.html bis zu 17-fach= 1700%
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| Function Name CPU Presler Northwood Yonah Dothan AMD64 AMD64 X2 Total StringReplace_JOH_IA32_4 1034 1010 1000 1000 1000 1000 6044 StringReplace_JOH_IA32_3 1000 1000 1104 1105 1049 1066 6324 StringReplace_JOH_PAS_4 1210 1202 1020 1029 1143 1140 6744 StringReplace_JOH_PAS_3 1316 1278 1105 1105 1236 1256 7296 StringReplace_EG_MMX_ 1376 1462 1053 1070 1165 1187 7313 StringReplace_SHA_IA32_1 2183 2236 1662 1709 1548 1618 10956 StringReplace_EWC_IA32_2 2202 2182 1726 1733 1668 1682 11193 StringReplace_DKC_SSE2_19 2243 2386 1759 1770 1859 1903 11920 StringReplace_DKC_SSE2_20 2255 2440 1759 1775 1864 1905 11998 StringReplace_DKC_SSE_10 2366 2420 1778 1792 1885 1903 12144 StringReplace_DKC_SSE_9 2364 2400 1789 1790 1921 1957 12221 StringReplace_DKC_IA32_15 2400 2364 1851 1861 1918 1946 12340 StringReplace_DKC_IA32_16 2410 2378 1852 1865 1923 1955 12383 StringReplace_DKC_MMX_9 2417 2414 1846 1851 1927 1971 12426 StringReplace_DKC_MMX_10 2427 2494 1836 1857 1934 1976 12524 StringReplace_DKC_IA32_14 2460 2420 1869 1882 1962 1997 12590 StringReplace_DKC_MMX_11 2487 2688 1824 1827 1915 1948 12689 StringReplace_DKC_Pas_5 2511 2536 2055 2059 1900 1924 12985 StringReplace_DKC_Pas_4 3660 4112 2989 3011 2523 2784 19079 StringReplace_HV_IA32_1 8224 8096 5904 6128 5533 5734 39619 StringReplace_REF_PAS_1 8188 8598 5892 6044 6076 6580 41378 StringReplace_RTL_PAS_1 17376 16298 15058 15722 11217 11501 87172 |
Gruss Horst
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Mo 28.08.06 13:44
Hi,
thnx, aber ich hatte gestern abend schon selber eine Funktion geschrieben. Zuerst so wie von BenBE vorgeschlagen, dann habe ich noch versucht, die Funktion zu optimieren, so dass nicht immer ein Zeichen nach dem anderen in den Puffer kopiert wird, sondern stattdessen eine Variable hochgezählt, anhand der dann alle (bisherigen) Zeichen auf einmal kopiert werden können:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
| char* strReplace(char* sCont, char* sSearch, char* sRepl) { int nCont = strlen(sCont); int nSearch = strlen(sSearch); int nRepl = strlen(sRepl); ; int nBuff = (nCont / nSearch) * nRepl + (nCont % nSearch); nBuff = (nBuff < nCont) ? nCont : nBuff; char* sBuff = malloc(sizeof(char) * nBuff); *sBuff = '\0'; ; int nToCopy = 0; while(*sCont) { if(*sCont == *sSearch) { char* pCont = sCont; char* pSearch = sSearch; while((*++pCont == *++pSearch) && (*pCont) && (*pSearch)); ; if(!*pSearch) { strncat(sBuff, (sCont - nToCopy), nToCopy); nToCopy = 0; strcat(sBuff, sRepl); sCont += nSearch; continue; } } sCont++; nToCopy++; } strncat(sBuff, (sCont - nToCopy), nToCopy); return sBuff; } |
Soweit ich es getestet habe, scheint die Funktion ok zu sein.
Die Frage ist jetzt, ob ich bei DevCpp einfach ein DLL-Projekt anlegen kann, die Funktion compilieren und dann die Funktion in Delphi einbinden kann?
Gruss aus Hamburg
Moderiert von Christian S.: Code- durch C#-Tags ersetzt
|
|
mkinzler
      
Beiträge: 4106
Erhaltene Danke: 13
Delphi 2010 Pro; Delphi.Prism 2011 pro
|
Verfasst: Mo 28.08.06 14:04
Zitat: |
Die Frage ist jetzt, ob ich bei DevCpp einfach ein DLL-Projekt anlegen kann, die Funktion compilieren und dann die Funktion in Delphi einbinden kann? |
Sollte gehen.
_________________ Markus Kinzler.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 28.08.06 14:14
@ 2cHH: Da Du mit PChars arbeitest, solltest Du ein zusätzliches Byte am Ende für den Nullterminator mitreservieren. Inwiefern strncat dies automatisch macht, weiß ich aber ATM nicht. Bei malloc muss er mit enthalten sein (Macht SetLength intern, weshalb ich das nicht mit aufgeführt hab).
Weiterhin scheint es mir so, als ob du Potetielle Buffer Overruns in deiner Routine hast, wenn das Suchmuster am Ende des Strings gefunden wird.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Mo 28.08.06 14:36
BenBE hat folgendes geschrieben: | @ 2cHH: Da Du mit PChars arbeitest, |
Sorry, da kann ich nicht ganz folgen.
Wieso PChar, in C gibt es imho nur eine Sorte Chars, von Unicode mal abgesehen?
BenBE hat folgendes geschrieben: | @ 2cHHein zusätzliches Byte am Ende für den Nullterminator mitreservieren....
Weiterhin scheint es mir so, als ob du Potetielle Buffer Overruns in deiner Routine hast, wenn das Suchmuster am Ende des Strings gefunden wird. |
strcat passt schon, das geht ja alles in den Buffer, der ist gross genug, Leerchars oder fehlende Chars gibt es da auch nicht.
Potentielle Buffer Overruns - Wie soll das genau passieren? das würde ich gerne verhindern.
Hehe, vorhin habe ich mich nicht genau ausgedrückt, ich exporiere die DLL, soweit ok,
aber was mache ich in Delpi, gibt es da ein DLL-Import Tutorial?
Oder ist Delphi so performant, dass es zu überlegen wäre, den Code auf Delphi zu portieren?
Gruss aus Hamburg
|
|
mkinzler
      
Beiträge: 4106
Erhaltene Danke: 13
Delphi 2010 Pro; Delphi.Prism 2011 pro
|
Verfasst: Mo 28.08.06 14:42
Zitat: | Wieso PChar, in C gibt es imho nur eine Sorte Chars, von Unicode mal abgesehen? |
Dieser Typ heißt in Delphi PChar ( Pointer to Char)
_________________ Markus Kinzler.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 28.08.06 14:51
2cHH hat folgendes geschrieben: | BenBE hat folgendes geschrieben: | @ 2cHH: Da Du mit PChars arbeitest, |
Sorry, da kann ich nicht ganz folgen.
Wieso PChar, in C gibt es imho nur eine Sorte Chars, von Unicode mal abgesehen? |
Jup, ABER ^^ Char* ist wird in Delphi als Zeiger auf ein Zeichen (Pointer to Char --> PChar) übersetzt. Und diese umfassen neben den Daten am Ende ein zusätzliches Byte zur Nullterminierung, welches Du zusätzlich mit reservieren musst.
2cHH hat folgendes geschrieben: | BenBE hat folgendes geschrieben: | ein zusätzliches Byte am Ende für den Nullterminator mitreservieren....
Weiterhin scheint es mir so, als ob du Potetielle Buffer Overruns in deiner Routine hast, wenn das Suchmuster am Ende des Strings gefunden wird. |
strcat passt schon, das geht ja alles in den Buffer, der ist gross genug, Leerchars oder fehlende Chars gibt es da auch nicht.
Potentielle Buffer Overruns - Wie soll das genau passieren? das würde ich gerne verhindern. |
Leerchars und Fehlende Chars mein ich auch nicht. Ich hab beim Überfliegen des Sources einen Teil deiner Abbruchbedingung übersehen, der EndOfString korrekt prüft. Würde dieser Teil fehlen, könnte es passieren, dass bei einem Match am Ende des String Daten hinter den zu vergleichenden Strings analysiert werden würden. Die Prüfung ist aber gegeben (bei näherem Hinsehen).
2cHH hat folgendes geschrieben: | Hehe, vorhin habe ich mich nicht genau ausgedrückt, ich exporiere die DLL, soweit ok,
aber was mache ich in Delpi, gibt es da ein DLL-Import Tutorial?
Oder ist Delphi so performant, dass es zu überlegen wäre, den Code auf Delphi zu portieren?
Gruss aus Hamburg |
Wenn man mit Delphi korrekt programmiert, kann es auch sehr performant sein ... Im Normalfall geht es aber mit C++ einen Tick schneller ...
Zum Import:
Delphi-Quelltext 1:
| Function DeinReplaceStr(sCont: PChar; sSearch: PChar; sReplace: PChar): PChar; external 'Deine.dll' name 'strReplace'; |
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Mo 28.08.06 14:57
BenBE hat folgendes geschrieben: | diese umfassen neben den Daten am Ende ein zusätzliches Byte zur Nullterminierung, welches Du zusätzlich mit reservieren musst. |
Übergeben wird ja ein Nullterminierter String (sBuff). Wenn ich es richtig verstanden habe,
hätte man die Problematik, wenn man einzelne Chars übergeben würde, nicht?
BenBE hat folgendes geschrieben: | Zum Import:
Delphi-Quelltext 1:
| Function DeinReplaceStr(sCont: PChar; sSearch: PChar; sReplace: PChar): PChar; external 'Deine.dll' name 'strReplace'; | |
Thnx, dann werde ich mal mein Glück versuchen....
Gruss aus Hamburg
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Mo 28.08.06 16:44
Hi,
die DLL habe ich jetzt fertig:
[url= img241.imageshack.us...1/replacedllig1.png] [/URL]
[url= img102.imageshack.us...placedlltestta1.png] [/URL]
Wie man sieht, lässt sich die DLL auch in das Projekt einbinden.
Leider bekomme ich unter Delphi keine korrekten Ergebnisse.
Man könnte denken, dass es an der DLL liegt, aber die ist wohl OK,
denn in C funktioniert das ganze, mit Ergebnis.
Bei Delphi muss es wohl Probleme it der Übergabe des Strings o.ä. geben,
mir wird bei jeder Eingabe immer nur:
Quelltext
zurückgegeben. Hier mein DLL-Aufruf:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| implementation
{$R *.dfm}
Function MyReplace(sSource: PChar; sSearch: PChar; sReplace: PChar): PChar; external 'replaceDLL.dll' name 'strReplace';
procedure TForm1.ButtonReplaceClick(Sender: TObject); begin pSource := PChar(RichEditSource.Text); pSearch := PChar(RichEditSearch.Text); pReplace := PChar(RichEditReplace.Text); pResult := MyReplace(pSource, pSearch, pReplace); RichEditSource.Text := AnsiString(pResult); end; |
Jibbet da irschendwälsche Driggs?
(Gibt es da irgendwelche Tricks)
Gruss aus Hamburg
|
|
mkinzler
      
Beiträge: 4106
Erhaltene Danke: 13
Delphi 2010 Pro; Delphi.Prism 2011 pro
|
Verfasst: Mo 28.08.06 16:53
Delphi verwendet eine andere Übergabekonvention. Verwende mal StdCall;
_________________ Markus Kinzler.
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Mo 28.08.06 17:11
mkinzler hat folgendes geschrieben: | Delphi verwendet eine andere Übergabekonvention. Verwende mal StdCall; |
Cool, jetzt funktioniert es, thnx.
*freu*
Gruss aus Hamburg
|
|
mkinzler
      
Beiträge: 4106
Erhaltene Danke: 13
Delphi 2010 Pro; Delphi.Prism 2011 pro
|
Verfasst: Mo 28.08.06 17:14
Im nächsten Schritt kannst du ja zu Übung die Funktion in Delphi implemnetieren 
_________________ Markus Kinzler.
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Mo 28.08.06 17:54
mkinzler hat folgendes geschrieben: | Im nächsten Schritt kannst du ja zu Übung die Funktion in Delphi implemnetieren |
Das wäre bestimmt eine gute Übung, ich glaube, dass mache ich auch.
Aber mit der DLL habe ich mich doch etwas zu früh gefreut:
Auf der Konsole funktioniert die DLL auch mit grösseren Strings mehrmals hintereinander:
[url= img100.imageshack.us...cedllconsolefu1.png] [/URL]
Bei dem GUI-Programm gibt es einen fehlerhaften Zugriffsversuch auf 0x00000000.
Die erste Ersetzung ist verflixt schnell, so wie ich mir das auch vorgestellt habe,
leider kann man danach keine zweite mehr machen und müsste das Programm neustarten.
Ich glaube, es könnte immer noch an dem DLL-Aufruf liegen, nur wo genau?
|
|
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: Mo 28.08.06 20:04
2cHH hat folgendes geschrieben: | mkinzler hat folgendes geschrieben: | Delphi verwendet eine andere Übergabekonvention. Verwende mal StdCall; |
Cool, jetzt funktioniert es, thnx. |
Hast du auch die C-Funktion als __stdcall markiert?
Zudem hast du da ein Speicherleck, denn der von dir zurückgegebene char* wird nicht mehr freigegeben. (und versuche erst gar nicht den mit FreeMem freizugeben; er wurde mit malloc() reserviert, muss also auch mit free() freigegeben werden)
_________________ Ist Zeit wirklich Geld?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 28.08.06 20:34
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
2cHH 
      
Beiträge: 46
|
Verfasst: Mo 28.08.06 20:46
Es funktioniert jetzt 1A, es war ein kleiner Fehler in der DLL, der grosse Auswirkungen hatte.
Das Proggi ist jetzt auch deutlich schneller, als mit AnsiReplaceStr.
*wieder freu*
Ja, stimmt, der Speicher wird solange reserviert, wie das Programm läuft.
In diesem Fall macht das aber nichts, weil das Programm nicht ständig laufen soll.
Es handelt sich um ein paar KB, die dann wieder freigegeben werden,
wenn das Programm beendet wird.
Trotzdem wäre es ganz interessant, das mal auszuprobieren.
Du meinst, Delphi hat auch eine Möglichkeit, um Speicher zu allozieren
und freizugeben, die mit der von C kombatibel ist?
Das wäre ja nicht schlecht. Werde ich nachher mal versuchen.
Die DLL brauche ich ja wohl nicht wie in C mit "Bool FreeLibrary(ModuleHandle)"
wieder freizugeben, ich denke das wird Delphi schon machen, oder?
So langsam fängt Delphi an, Spass zu machen
---------------
ps:
BenBE hat folgendes geschrieben: | Tipp: Schreib deine Funktion so um, dass der Ausgabepuffer und dessen Speichergröße von der Anwendung reserviert werden muss: |
Sorry, das haben wir parallel gepostet.
Deine Quelltexte werde ich mir auf jeden Fall kopieren,
da kann ich bestimmt auch noch was von lernen.
ps2: Du hattest ganz zu Anfang übrigens Recht, da hat ein Byte gefehlt.
Die Funktion strlen rechnet in C nicht das '\0' mit.
Dann war noch ein Division durch 0 möglich, die ich jetzt vorher abfange.
Gruss aus Hamburg
|
|
|