ReplaceString(), FindString() unschlagbar?

Für allgemeine Fragen zur Programmierung mit PureBasic.
SMaag
Beiträge: 152
Registriert: 08.05.2022 12:58

ReplaceString(), FindString() unschlagbar?

Beitrag von SMaag »

Es kommt immer wieder die Meinung auf, das PB-Stringfunktionen langsam sind.
Da bei ReplaceString() und FindString() immer die Strings kopiert werden, dachte ich, man kann die
PB-Funktionen bestimmt schlage, vor allem, wenn man nur 1 Zeichen statt eines längeren Strings ersetzen will.
Aber weit gefehlt! Den PB-Compiler schlägt man nur in einem einzigen Fall. Sonst verliert man mit eigenem Code immer!

Der Fall den man gegen ReplaceString() gewinnt, ist wenn man nur einzelne Character ersetzen will und man
direkt mit einem String-Pointer arbeitet.
Soblad man einen String auch nur eine eine Funktion übergibt hat man bereits verloren.
Mich würde mal interssieren, was PB da intern macht!

Hier mein TestCode mit der Zeiterfassung für 1.000.000 Aufrufe
Achtung: Complierung ohne Debugger oder Exe erstellen, sonst macht das keinen Sinn

PB-ReplaceString 114ms
RelaceChar Procedure 27ms
ReplaceChar Macro 25ms
leerer Funcitonsaufruf MyReplaceString 232ms, selbst wenn man die Stringrückgabe entfernt sind es noch 150ms

Code: Alles auswählen

  EnableExplicit
  Structure pChar   ; virtual CHAR-ARRAY, used as Pointer to overlay on strings 
    c.c[0]          ; fixed ARRAY Of CHAR Length 0
  EndStructure

  
 Procedure ReplaceChar_(*String, cSearch.c, cReplace.c)
    Protected *char.Character   ; Pointer to a virutal Char-Struct
    
    *char = *String         
    If *char    
      While *char\c               ; until end of String
        If *char\c =  cSearch
          *char\c = cReplace    ; replace the Char
        EndIf
        *char + 2                    ; Index to next Char
      Wend
    EndIf
  EndProcedure
  
 Macro ReplaceChar(String, cSearch, cReplace)
    Define *char.Character   ; Pointer to a virutal Char-Array
    
    *char = @String         ; overlay the String with a virtual Char-Array
    If *char    
      While *char\c            ; until end of String
        If *char\c =  cSearch
          *char\c = cReplace   ; replace the Char
        EndIf
        *char + 2                    ; Index to next Char
      Wend
    EndIf
 EndMacro

  
 Procedure.s MyReplaceString(String$, Search$, Replace$)   
   ; Das bringt nichts, da bereits der leere Funktionsaufruf
   ; langsamer ist als PureBasic ReplaceString()
    ProcedureReturn String$ ; Returns the number of replaced Chars
EndProcedure

  Define s$, res$, t_pb, t_my, t_mac, t_str, K, p , N =1000000
  s$ = "Ich bin ein String und war ein String"
  
;   For K = 0 To 5
;     s$ =s$ + s$
;   Next
  
  ; PureBasic
  t_pb = ElapsedMilliseconds()
  For K=0 To N
    res$ =  ReplaceString(s$, "e", "E")
  Next
  t_pb = ElapsedMilliseconds()-t_pb
  Debug "PB-Replace : " + t_pb + "ms"
  Debug res$
  Debug ""
  
  ; MyFunction
  t_my = ElapsedMilliseconds()
  For K=0 To N/2
    ReplaceChar_(@s$,'e', 'E')      ; um fair zu bleiben, müssen wir das wieder zurück tauschen
    ReplaceChar_(@s$,'E', 'e')      ; dafür dann aber nur die halbe Schleifenanzahl
  Next
  t_my = ElapsedMilliseconds()-t_my
  Debug "MyReplaceFunction : " + t_my + "ms"
  Debug res$
  Debug ""
  
  ; MyMacro
  t_mac = ElapsedMilliseconds()
  For K=0 To N/2
    ReplaceChar(s$,'e', 'E')
    ReplaceChar(s$,'E', 'e')
  Next
  t_mac = ElapsedMilliseconds()-t_mac
  Debug "MyReplaceMacro : " + t_mac + "ms"
  Debug ""
  
    ; MyMacro
  t_str = ElapsedMilliseconds()
  For K=0 To N
    res$ =  MyReplaceString(s$, "e", "E")
  Next
  t_str = ElapsedMilliseconds()-t_str
  Debug "EmptyFunction : " + t_str + "ms"
  Debug ""

  
  MessageRequester("Ergebnis", "PB : " + t_pb + "ms" + #CRLF$ + "MyFunction : " + t_my + "ms" + #CRLF$ + "MyMacro : " + t_mac + "ms" + #CRLF$ + "EmptyFunction : " + t_str + "ms" + #CRLF$)
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: ReplaceString(), FindString() unschlagbar?

Beitrag von NicTheQuick »

Mein Ergebnis ist das hier:
PB: 96ms
MyFunction: 29ms
MyMacro: 26ms
EmptyFunction: 80ms
Blöde Frage, aber warum ist dein Code so komisch eingerückt, dass vor jeder Zeile zwei Leerzeichen sind?

Und hier noch ein kleiner Trick, damit man nicht @ benutzen muss um einen String als Pointer zu übergeben:

Code: Alles auswählen

Prototype ReplaceChar(String.p-unicode, cSearch.c, cReplace.c)

Procedure ReplaceChar_(*char.Character, cSearch.c, cReplace.c)
	Debug "*char = " + StrU(*char)
	While *char\c
		If *char\c = cSearch
			*char\c = cReplace
		EndIf
		*char + SizeOf(Character)
	Wend
EndProcedure

Define ReplaceChar.ReplaceChar = @ReplaceChar_()


Define myString.s = "Hallo"
Debug "@myString = " + StrU(@myString)

ReplaceChar(myString, 'l', 'L')

Debug myString
Bild
SMaag
Beiträge: 152
Registriert: 08.05.2022 12:58

Re: ReplaceString(), FindString() unschlagbar?

Beitrag von SMaag »

Prototype ReplaceChar(String.p-unicode, cSearch.c, cReplace.c)
Define ReplaceChar.ReplaceChar = @ReplaceChar_()
Ich versteh's nicht!
1. was ist p-Unicode!
Das sieht wieder nach einem undokumentierten vordefinierten Typ aus!
Ist es aber nicht, da Declare s.p-Unicode und dann s\ nicht möglich ist.

2. Define ReplaceChar.ReplaceChar = @ReplaceChar_()
wenn ich es richtig verstehe, dann wird hier der gewüschte Aufrufnahme
welcher ReplaceChar sein soll auf den Prototype ReplaceChar umgeleitet
und die Aufrufadresse von ReplaceChar_ zugewiesen.
Also 'Aufrufnahme'.'PrototypName' = Funktionsadresse

3. ReplaceChar(myString, 'l', 'L')
das ist dann der umgeleitete Funktionsaufruf über ProtoType
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: ReplaceString(), FindString() unschlagbar?

Beitrag von NicTheQuick »

Zu Punkt 1: Schau mal in die Hilfe zu Prototype und klick dich dann zu Pseudotypes. Die können noch mehr coole Sachen wie z.B. implizites Kodieren zu UTF-8 oder ähnliches.

Die anderen zwei Punkte hast du korrekt verstanden.
Bild
SMaag
Beiträge: 152
Registriert: 08.05.2022 12:58

Re: ReplaceString(), FindString() unschlagbar?

Beitrag von SMaag »

Ich hab das Prinzip kapiert!

Es geht auch mit
Prototype ReplaceChar(String$, cSearch.c, cReplace.c)
was ich jetzt mehr korrekt finden würde! Ich ja nichts wandeln will und muss. Und wenn alles ASCII im ASCII mode laufen würde,
gäb es auch keine Fehler.

Lieg ich da richtig?
SMaag
Beiträge: 152
Registriert: 08.05.2022 12:58

Re: ReplaceString(), FindString() unschlagbar?

Beitrag von SMaag »

jetzt hab ich auch verstanden, warum ein leerer Funktionsaufruf bereits länger dauert als der
Aufruf von ReplaceString()

das ganze nun mit Pointer und Prototype und schon ist das weit schneller 33ms statt 150ms

Code: Alles auswählen

Prototype.s MyReplaceString(String$, Search$, Replace$)

 Procedure.s MyReplaceString_(*String.Character, *Search.character, *Replace.character)   
   ; mit dem Protype geht's jezt wesentlich schneller, da werden die Strings nicht
   ; nochmal kopiert
   ProcedureReturn #Null$
EndProcedure

Define MyReplaceString.MyReplaceString = @MyReplaceString_()

Das bedeuted aber, wenn man schnelle Stringfunktionen haben möchte, dann müsste man immer
die Funktion mit Pointern definieren und dann einen Prototype drübersetzen
Benutzeravatar
mk-soft
Beiträge: 3701
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: ReplaceString(), FindString() unschlagbar?

Beitrag von mk-soft »

Strings in Parameters werden als ByVal übergeben. Somit immer eine Kopie des String.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: ReplaceString(), FindString() unschlagbar?

Beitrag von NicTheQuick »

mk-soft hat geschrieben: 18.11.2022 15:29Strings in Parameters werden als ByVal übergeben. Somit immer eine Kopie des String.
Mit dem Prototype-Ansatz eben nicht. :)

Ich habe deswegen in meinem Beispiel extra außerhalb und innerhalb der Procedure den Pointer des Strings ausgegeben, damit man sieht, dass er gleich ist.
Bild
SMaag
Beiträge: 152
Registriert: 08.05.2022 12:58

Re: ReplaceString(), FindString() unschlagbar?

Beitrag von SMaag »

Hab das mit den Prototypes jetzt mal in einem Modul versucht!
Geht! Aber muss hier _AddQuotes_ unbedingt Public sein, oder bekommt man
das mit einem Trick auch Private? Hab schon etwas rumprobiert, hat aber nicht funktioniert!

Code: Alles auswählen

DeclareModule Quotes
  #STR_CHAR_DoubleQuote = 34    ; "
  #STR_CHAR_SingleQuote = 39    ; '
  
  
  Declare.s AddQuotes_Classic(String$, cQuote.c=#STR_CHAR_DoubleQuote)

  Prototype.s AddQuotes(String$, cQuote.c=#STR_CHAR_DoubleQuote)
  Declare.s _AddQuotes_(*String, cQuote.c=#STR_CHAR_DoubleQuote) ; bekommt man _AddQuotes_ auch Private?
  Define AddQuotes.AddQuotes = @_AddQuotes_()
EndDeclareModule


Module Quotes

  Procedure.s AddQuotes_Classic(String$, cQuote.c=#STR_CHAR_DoubleQuote)
   ; ASCII-34 = ", ASCII-39 = '
    
   ProcedureReturn Chr(cQuote) + PeekS(@String$) + Chr(cQuote)
   ;ProcedureReturn Chr(cQuote) + String$ + Chr(cQuote)      
   EndProcedure
    
  Procedure.s _AddQuotes_(*String, cQuote.c=#STR_CHAR_DoubleQuote)  ; 
    ; ASCII-34 = ", ASCII-39 = '
    ProcedureReturn Chr(cQuote) + PeekS(*String) + Chr(cQuote)
  EndProcedure
   
EndModule

UseModule Quotes
Define s$, N, K, t_C, t_P, t_m

N = 100000  
s$ = "Ich bin ein String und war ein String"

t_C = ElapsedMilliseconds()
For K=0 To N
  res$ = AddQuotes_Classic(s$)
Next
t_C = ElapsedMilliseconds()-t_C
Debug "Classic : " + t_C + "ms"
Debug res$
Debug ""

t_P = ElapsedMilliseconds()
For K=0 To N
  res$ =  AddQuotes(s$)
Next
t_P = ElapsedMilliseconds()-t_P
Debug "Prototype : " + t_P + "ms"
Debug res$
Debug ""


MessageRequester("Ergebnis", "Classic : " + t_C + "ms" + #CRLF$ + "Prototype : " + t_P )

Axolotl
Beiträge: 154
Registriert: 31.12.2008 16:34

Re: ReplaceString(), FindString() unschlagbar?

Beitrag von Axolotl »

Code: Alles auswählen

Define AddQuotes.AddQuotes = @_AddQuotes_()
Ich habe da mal eine Frage: Kann man die Procedure so auch in anderen Proceduren benutzen?
Ich verwende eigentlich immer Global oder Protected und Define dann in Verbindung mit Shared.
Mostly running PureBasic <latest stable version and current alpha/beta> (x64) on Windows 11 Home
Antworten