Umlaute ersetzen, geht das schneller ?

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von GPI »

Stargate, welche PB-Version benutzt du?
weil PB 3.70

Code: Alles auswählen

Debug $FFFF
Debug 'Ä'
Debug 'Ä' & $Ffff
ergibt

Code: Alles auswählen

65535
12779652
132
Mit 3.70 funktioniert dein Code überhaupt nicht. Erzeugt nur nonsense.

Wobei ich hier von einen heftigen Bug in 3.7 ausgehe...

Man sollte aber generell überlegen, dass das Lookup-Array stolze 256 MB frisst.
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von STARGÅTE »

>> Stargate, welche PB-Version benutzt du?
PB 5.70
Version 3.70 ist schon sehr alt...

>> Man sollte aber generell überlegen, dass das Lookup-Array stolze 256 MB frisst.
Wie kommst du darauf?
$FFFF * SizeOf(Long) = 262 kB
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von GPI »

oh, meinte 5.7

Komisch, bei mir flippt PureBasic hier völlig aus. Der code läuft nicht.

Und ja, hab mich verrechnet. Ich halte aber 262KB für so eine Tabelle auch schon für unfug.

Edit: Fehler gefunden...
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von GPI »

Code: Alles auswählen

;Debugger aus !!!


; Initialisierung
Global Dim LookupTable.l($FFFF)
Define Ii
For I= 0 To $FFFF
  LookupTable(I) = I
Next
LookupTable('Ä') = 'eA'
LookupTable('Ö') = 'eO'
LookupTable('Ü') = 'eU'
LookupTable('ä') = 'ea'
LookupTable('ö') = 'eo'
LookupTable('ü') = 'eu'
LookupTable('ß') = 'ss'
LookupTable($1E9E) = 'SS'


; STARGÅTEs Prozedur
Procedure.s FastReplaceUmlaute(*Text.Character)
  
  Static *Buffer 
  If *text=0
    If *Buffer
      FreeMemory(*buffer)
    EndIf
    *buffer=0
    ProcedureReturn ""
  EndIf  
  
  *Buffer = ReAllocateMemory(*Buffer, MemoryStringLength(*Text, #PB_ByteLength)*2+2,#PB_Memory_NoClear)
  Protected *Position.Long = *Buffer
  
  While *Text\c
    *Position\l = LookupTable(*Text\c)
    If *Position\l & ~$FFFF
      *Position + SizeOf(Character)*2
    Else
      *Position + SizeOf(Character)
    EndIf
    *Text+ SizeOf(Character)
  Wend
  *Position\l=0
  
  
  ProcedureReturn PeekS(*Buffer, (*Position-*Buffer)/SizeOf(Character))
  
EndProcedure

Structure CharacterDouble
  StructureUnion
    c.c
    l.l
  EndStructureUnion
EndStructure

Procedure.s ReplaceUmlauteGPI(*text.Character)
  Static *buffer, Buffersize
  
  If *text=0
    If *buffer
      FreeMemory(*buffer)
    EndIf
    *buffer=0
    Buffersize=0
    ProcedureReturn ""
  EndIf
  
  Protected textlen=MemoryStringLength(*text,#PB_ByteLength) *2+2
  
  If Buffersize<textlen
    textlen+1024;damit nicht dauernd neu angefordert wird
    *buffer=ReAllocateMemory(*buffer,textlen,#PB_Memory_NoClear)
    Buffersize=textlen
  EndIf
  
  
  Protected *out.CharacterDouble=*buffer
  While *text\c        ;1.Zeichen im Text bei Start
    
    Select *text\c
      Case 0 To 127:*out\c=*text\c:*out+SizeOf(character)
        
      Case 'Ä' : *out\l='eA':*out+SizeOf(long)
      Case 'Ö' : *out\l='eO':*out+SizeOf(long)           
      Case 'Ü' : *out\l='eU':*out+SizeOf(long)
      Case 'ä' : *out\l='ea':*out+SizeOf(long)
      Case 'ö' : *out\l='eo':*out+SizeOf(long)           
      Case 'ü' : *out\l='eu':*out+SizeOf(long)
      Case 'ß' : *out\l='ss':*out+SizeOf(long)           
      Case $1E9E:*out\l='sS':*out+SizeOf(long)
      Default:*out\c=*text\c:*out+SizeOf(character)
    EndSelect     
    *text + SizeOf(Character) ;zum nächsten Zeichen im Text
    
  Wend
  *out\c=0
  
  
  ProcedureReturn PeekS(*buffer)
EndProcedure


Procedure.s ReplaceUmlaute(*text.Character)
  
  ;Ä ä Ö ö Ü ü ß "
  ;AeaeOeoeUeuess"
  
  Static mem 
  If *text=0
    If mem
      FreeMemory(mem)
    EndIf
    mem=0
    ProcedureReturn ""
  EndIf
  
  Protected newlg = MemoryStringLength(*text, #PB_ByteLength) * 2+2
  
  
  mem= ReAllocateMemory(mem,newlg,#PB_Memory_NoClear);nicht jedesmal neu allocate!
  
  Protected new$,  *buff.Character=mem
  
  While *text\c        ;1.Zeichen im Text bei Start
    
    Select *text\c
      Case 0 To 127  : *buff\c = *text\c ;Speedup!       
      Case 'Ä' : *buff\c = 'A' :  *buff + SizeOf(Character) : *buff\c = 'e'
      Case 'Ö' : *buff\c = 'O' :  *buff + SizeOf(Character) : *buff\c = 'e'
      Case 'Ü' : *buff\c = 'U' :  *buff + SizeOf(Character) : *buff\c = 'e'
      Case 'ä' : *buff\c = 'a' :  *buff + SizeOf(Character) : *buff\c = 'e'
      Case 'ö' : *buff\c = 'o' :  *buff + SizeOf(Character) : *buff\c = 'e'
      Case 'ü' : *buff\c = 'u' :  *buff + SizeOf(Character) : *buff\c = 'e'
      Case 'ß' : *buff\c = 's' :  *buff + SizeOf(Character) : *buff\c = 's'             
      Default  : *buff\c = *text\c               
    EndSelect     
    *text + SizeOf(Character) ;zum nächsten Zeichen im Text
    *buff + SizeOf(Character) ;zum nächsten Zeichen im Textbuffer
    
  Wend
  *buff\c=0
  
  ProcedureReturn PeekS(mem)
EndProcedure

Dim text$(5):maxtext=6
text$(0)="Über Äpfel Rüben Straße ärgert Ösil + Bär"
text$(1)="abcdefghijklmnopqrstuvwxyzäöüßABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜß"
text$(2)="Ein Text ohne Umlaute"
text$(3)="ääääööööööööööüüüüüüüüüüüüüüßßßßßßßßßßßßßßßÄÄÄÄÄÄÄÄÄÄÄÖÖÖÖÖÖÖÖÖÖÖÖÜÜÜÜÜÜÜÜÜÜÜÜ"
text$(4)="!§&/)(&/)(%/!(&)/(19263789126398463978"
text$(5)="aÄbÄcÖeÖdÜeß"

max = 100000*10


s = ElapsedMilliseconds()
For j = 1 To max
  xx$ = ReplaceString(ReplaceString(ReplaceString(ReplaceString(ReplaceString(ReplaceString(ReplaceString(text$(j%maxtext), "Ä", "Ae"), "Ü", "Ue"), "Ö", "Oe"), "ä", "ae"), "ü", "ue"), "ö", "oe"), "ß", "ss")
Next
ss = ElapsedMilliseconds() - s

s = ElapsedMilliseconds()
For j = 1 To max
  new$ = ReplaceUmlaute(@text$(j%maxtext))
Next
ReplaceUmlaute(0)
su = ElapsedMilliseconds() - s

s = ElapsedMilliseconds()
For j = 1 To max
  fast$ = FastReplaceUmlaute(@text$(j%maxtext))
Next
FastReplaceUmlaute(0)
sv = ElapsedMilliseconds() - s

s = ElapsedMilliseconds()
For j = 1 To max
  fastGPI$ = ReplaceUmlauteGPI(@text$(j%maxtext))
Next
ReplaceUmlauteGPI(0)
sg = ElapsedMilliseconds() - s

info$ + #LF$
info$ + xx$  + " -PB:" + #TAB$ + Str(ss) + #LF$

info$ + new$ + " -ich" + #TAB$ + Str(su) + #LF$

info$ + fast$ + " -STARGÅTE" + #TAB$ + Str(sv) + #LF$

info$ + fastGPI$ + " -gpi" + #TAB$ + Str(sg) + #LF$

MessageRequester("", info$)
Ich hab mal meine eigene Variante geschrieben. Die Ergebnisse sind lustig, zumindest bei mir.

Ich hab mir nicht nehmen lassen, alle Varianten anzugleichen. Der große Vorteil der "FAST"-Routine ist, das er den Speicher nicht immer neu anfordert, sondern wiederverwendet. Das beschleunigt am meisten, nicht die Lookup-Tabelle.
Ich hab bei allen Varianten auch hinzugefügt, das der angeforderte Speicher nicht mehr gelöscht wird. Dafür muss ich jetzt natürlich das Null-Abschluss-Byte setzen.
Genauso hab ich die Variable "new$" gekillt, sondern gebe direkt bei Return das Peeks() zurück. Das spart umkopieren von Strings.
Um den dauerhaft reservierten Speicher wieder freizugeben, kann man die Funktion mit 0 als Parameter aufrufen. Das hab ich mal beim Messen am Schluss eingefügt.
Meine Routine geht mit der Speicherreservierung etwas klüger um. Wenn der Buffer schon groß genug ist, wird er nicht verkleinert. Zudem reserviert er +1024 Bytes, damit nicht immer wieder neu angefordert wird, wenn der nächste String ein paar Byte größer wird.
Was auch beschleunigt hat, als ersten Case 0-127 einzufügen. Das sind die normalen englischen Zeichen und Satzzeichen. Damit muss zu 99% nur ein Fall getestet werden. Geht schneller.

Den Benchmark hab ich angepasst, das er verschieden lange strings hernimmt. Und 10x mehr durchläufe gibt es, weil sonst die Ergebnisse zu nah beieinander liegen.

Die Zeiten liegen jetzt deutlich näher zusammen. Meine Lösung ist, dank weniger Speicherfreigaben, aktuell die schnellste :)
Zuletzt geändert von GPI am 24.03.2019 13:37, insgesamt 2-mal geändert.
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von STARGÅTE »

Wäre nett, wenn du nicht einfach den Code der anderen änderst, und dadurch Fehler einbaust.
Dein "*Position\l=0" am Ende meiner Schleife führt zum Speicherüberlauf, wenn der String komplett aus Umlauten besteht, weil du dann genau Länge*2 an Platz brauchst, dann aber noch ein Character darüber schreibst. Das selbe bei hjbremers Code.
Bei dir fällt das nicht auf, weil du eh immer 1024 Byte mehr reservierst.
Bitte passe das an.

>> Ich hab mir nicht nehmen lassen, alle Varianten anzugleichen. Der große Vorteil der "FAST"-Routine ist, das er den Speicher nicht immer neu anfordert, sondern wiederverwendet. Das beschleunigt am meisten, nicht die Lookup-Tabelle.
Das stimmt. Wobei das entscheidende auch das #PB_Memory_NoClear ist!
Die Tabelle zeigt ihre stärken erst, wenn mehr Fälle hinzukommen und/oder die Fälle gleichmäßiger verteilt sind.
Solange ein String sehr sehr wenig Umlaute besitzt, ist ein If oder Case schneller.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von GPI »

Ok, ich dachte bei #PB_ByteLength ist die Null dabei. Ist es wohl nicht.
Bei hjbremers Code ist der Fehler vorher auch schon drin. Da gibts ein Überlauf, weil Peeks keine Länge da hat.

Der größte Geschwindigkeitsgewinn aus meinen Code ist halt der, das ich nicht immer Reallocate aufrufe. Ich wollte halt den Code vergleichbar machen. Der größte Geschwindigkeitsgewinn von ursprünglichen Code kommt halt von Speichermanagment, nicht von ersetzen. Das wollte ich halt aufzeigen.

Die Frage ist halt auch, wie genau der Einsatzzweck ist. Wenns normaler Text ist, dann kommen relativ wenige Umlaute vor. In diesen Absatz kommt beispielsweise nicht einer vor.

Das sollte man auch beachten. Die die Idee ist sicherlich nicht schlecht (ich hab mir da die StruktureUnion quasi abgeschaut), nur halte ich es hier für übertrieben. Geschwindigkeit bringt aber hier das Speichermanagment. Wenn man das überall angleicht und die Funktion mehrmals aufruft, ist eigentlich immer eine andere Funktion schneller.

p.s.: Umlaut in diesen Text, wenn ich mich nicht verzählt habe: 10 Zeichen.
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
Benutzeravatar
Josh
Beiträge: 1028
Registriert: 04.08.2009 17:24

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von Josh »

@hjbremer

Wo kommt den der Text überhaupt her?
  • Wenn der Text vom User deines Programmes eingegeben wurde, dann handelt es sich wahrscheinlich um einen relativ kurzen Text und es steht nicht dafür, einen großen Aufwand zum Beschleunigen zu betreiben. Da kannst du auch die Pb Stringfunktionen verwenden.
  • Wenn du den Text von irgendwo einliest, dann ist die Chance, dass der Text in Utf8 vor liegt relativ groß. Wenn das zu trifft, solltest du erst mal da ansetzen, da die Umwandlung von Utf8 in Pb-Unicode und zurück schon relativ aufwändig ist. Da solltest du dann den Text nicht in einen String einlesen, sonder den Text mit ReadData als Utf8 in den Speicher lesen und von dort deine Änderungen vornehmen. Umlaute und das 'ß' werden in Utf8 ja noch als Ascii-Zeichen interpretiert, also wäre das ganz einfach zu handhaben.
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von GPI »

hmmm... stimmt, Text in UTF8-Format in Speicher laden, dann die UTF8-Sequenz für äöüß raussuchen (kurz geschaut, ä ist c3 84 laut https://www.utf8-zeichentabelle.de/unic ... 8-table.pl und lässt sich so perfect in ae ändern) und dann den Text mittel Peeks(*me,#pb_ascii) einlesen. Müsste die mit abstand schnellste Methode sein.

Allerdings nur, wenn er nicht auch noch den unverfälschten Text braucht.
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
Benutzeravatar
Andre
PureBasic Team
Beiträge: 1765
Registriert: 11.09.2004 16:35
Computerausstattung: MacBook Core2Duo mit MacOS 10.6.8
Lenovo Y50 i7 mit Windows 10
Wohnort: Saxony / Deutscheinsiedel
Kontaktdaten:

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von Andre »

Nur mal so am Rande, da ich eine gleiche/ähnliche Aufgabenstellung hatte und im engl. Forum entsprechend Hilfe bekommen habe, der Hinweis auf einen entsprechenden Thread dort:
Convert all special chars in a text into regular letters...
Bye,
...André
(PureBasicTeam::Docs - PureArea.net | Bestellen:: PureBasic | PureVisionXP)
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Re: Umlaute ersetzen, geht das schneller ?

Beitrag von hjbremer »

Josh hat geschrieben:@hjbremer

Wo kommt den der Text überhaupt her?
Mein Beispiel war nur ein Beispiel für die Allgemeinheit.
In der Realität kommt eine entsprechende ReplaceRoutine in einen Callback der der den Text in einem Editorgadget bearbeitet.
Und je schneller die Routinen Sind, desto mehr kann ich dort machen ohne es zu merken.

PS: Danke für den Hinweis mit +2 Zeichen für AllocateMemory.
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Antworten