Schneller Len(String$) Befehl

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.
Hellhound66
Beiträge: 476
Registriert: 23.03.2005 23:19

Schneller Len(String$) Befehl

Beitrag von Hellhound66 »

Code: Alles auswählen


;#PB 4.0
Prototype HellLen(UserString.s)
Global HellLen.HellLen

Goto __JUMPOVER
__HellLen:
!PUSH Edi
!PUSH Ecx
!MOV Edi,[Esp+12]
!XOR Ecx,Ecx
!XOR Ebx,Ebx
!__OUTERLOOP:
!MOV Eax,dword[Edi]
!TEST Eax,255
!JZ __FINISHED
!INC Ecx
!TEST Eax,65280
!JZ __FINISHED
!INC Ecx
!SHR Eax,16
!TEST Eax,255
!JZ __FINISHED
!INC Ecx
!TEST Eax,65280
!JZ __FINISHED
!INC Ecx
!ADD Edi,4
!JMP __OUTERLOOP
!__FINISHED:
!MOV Eax,Ecx
!POP Ecx
!POP Edi
!RET 4
__JUMPOVER:

HellLen = ?__HellLen

Delay(100)
string.s = "Ich bin ein fieser Teststring"
TestZeit.l  = ElapsedMilliseconds()
For i=1 To 10000000
    HellLen(string)
Next
Zeit1 = ElapsedMilliseconds()-TestZeit

TestZeit.l  = ElapsedMilliseconds()
For i=1 To 10000000
    lstrlen_(string)
Next
Zeit2 = ElapsedMilliseconds()-TestZeit

TestZeit.l  = ElapsedMilliseconds()
For i=1 To 10000000
    Len(string)
Next
Zeit3 = ElapsedMilliseconds()-TestZeit


MessageRequester("Ergebnis","Es wurden je 10000000 Aufrufe gestartet"+#crlf$+"Hellhound66 : "+Str(Zeit1)+" ms."+#crlf$+"WinAPI brauchte : "+Str(Zeit2)+" ms."+#crlf$+"PB brauchte : "+Str(Zeit3)+" ms."+#crlf$,#PB_MessageRequester_Ok)
Keine Garantie, dass der unter PB<4.0 läuft.

/edit:
Auf P4 HT kanns langsamer sein, aber auf nem Athlon isses eher schnell.
Optimismus ist ein Mangel an Information.
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Beitrag von ts-soft »

Bei Unicode ergibts immer 1
Das machen die langsameren besser :wink:
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
edel
Beiträge: 3667
Registriert: 28.07.2005 12:39
Computerausstattung: GameBoy
Kontaktdaten:

Beitrag von edel »

Code: Alles auswählen

CompilerIf #PB_Compiler_Unicode
CompilerElse
  Macro Len(string)
    HellLen(string)
  EndMacro 
CompilerEndIf
Wer braucht schon unicode ... :lol:
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

Hells Code für Unicode:

Code: Alles auswählen

Prototype FakeRemi(UserString.s) 
Global FakeRemi.FakeRemi

Goto __JUMPOVER2
__fakeremi: 
!MOV Edx,[Esp+4] 
!XOR Eax, Eax 
!__OUTERLOOP2: ;>
  !MOV Ecx, dword[Edx]
  CompilerIf #pb_compiler_unicode
  !TEST Ecx, 0xFFFF
  !JZ __FINISHED2
  
  !INC Eax
  !TEST Ecx, 0xFFFF0000 
  !JZ __FINISHED2
  
  !INC Eax 
  CompilerElse
  !TEST Ecx, 0xFF
  !JZ __FINISHED2
  
  !INC Eax 
  !TEST Ecx, 0xFF00 
  !JZ __FINISHED2
  
  !INC Eax 
  !TEST Ecx, 0xFF0000
  !JZ __FINISHED2
  
  !INC Eax 
  !TEST Ecx, 0xFF000000
  !JZ __FINISHED2
  
  !INC Eax
  CompilerEndIf
  !ADD Edx, 4 
!JMP __OUTERLOOP2 ;<
!__FINISHED2: 
!RET 4 
__JUMPOVER2: 

FakeRemi = ?__fakeremi
Benutzeravatar
nco2k
Beiträge: 892
Registriert: 08.09.2004 23:13

Beitrag von nco2k »

ouch, ein speedvergleich anhand ElapsedMilliseconds(). verwende lieber timeGetTime_() und benütze timeBeginPeriod_(1) am anfang deines programms und timeEndPeriod_(1) am ende deines programms.

auf meinem p4 ist deine variante etwa ~5ms schneller.

c ya,
nco2k
~|__/
..o.o.. <--- This is Einkaufswagen. Copy Einkaufswagen into your signature to help him on his way to world domination.
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

Nimm einfach mehr Durchläufe. Die Zeiten sollten grösser als 1 Sek. sein.
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Beitrag von Helle »

Zur allgemeinen Ergötzung hier ein Beispiel mit MMX/SSE (ohne Unicode).
So richtig Spass machts mit längeren Strings.

Code: Alles auswählen

;- Zeigt Einsatz von MMX und SSE zur Ermittlung von Len(String)
;- Auf Feinheiten wie Überprüfung auf Verfügbarkeit und Sicherungen der Umgebung wurde verzichtet

Global X.l
Global Y.l
Global Len.l

Global String.s = "Ich bin ein fieser Teststring"
 
 TestZeit.l  = ElapsedMilliseconds() 

 X = @String                 ;Zeiger auf den String

For i = 1 To 10000000 
 !mov ebx,[v_X]
 W0: 
 !movq mm0,[ebx]             ;8 Byte des Strings einlesen
 !add ebx,8
 !pxor mm1,mm1               ;mm1 auf Null setzen
 !pcmpeqb mm0,mm1            ;jedes der 8 Byte von mm0 mit Null (mm1) vergleichen 
 !cvtpi2pd xmm0,mm0          ;Konvertierung von Integer in Double für nachfolgenden Vergleich
 !pxor xmm2,xmm2             ;xmm2 auf Null setzen
 !ucomisd xmm0,xmm2          ;vergleicht die 2 Doubles in den unteren Quad-Words  
 !jz l_w0

 !sub ebx,8                  ;Zeiger in String 8 Byte zurück und Ende suchen
 !mov edx,[v_X]
 !sub edx,ebx
 !jnc l_w1
 !sub ebx,4

W1:
 !mov eax,[ebx] 
 !test eax,0ffh 
 !jz l_w2
 !inc ebx
 !test eax,0ff00h
 !jz l_w2
 !inc ebx
 !test eax,0ff0000h
 !jz l_w2
 !inc ebx
 !test eax,0ff000000h
 !jz l_w2 
 !inc ebx 
 !jmp l_w1 
W2:
Next 

!mov [v_Y],ebx              ;"nur" für Anzeige der Stringlänge

Zeit = ElapsedMilliseconds() - TestZeit 

 !mov edx,[v_Y]
 !sub edx,[v_X]
 !mov [v_Len],edx     

MessageRequester("Len(String) mit SSE und MMX","Stringlänge = " + Str(Len) + Chr(13) + "Testzeit = " + Str(Zeit) + " ms")

End
Gruss
Helle

/Edit: Mindestlänge String aufgehoben und optimiert, FAsm-Data-Section eingespart
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Beitrag von Helle »

Hier noch eine Version ohne MMX, nur mit SSE2. Greift gut bei längeren Strings, deshalb wurde als Teststring ein aktuelles Beispiel aus der PureBasic-Lounge genommen. Werte bei mir: "Denise": 1.030ms, SSE2: 610ms.

Code: Alles auswählen

;- Helle 08.10.2006 PB4.00
;- Zeigt Einsatz von SSE2 zur Ermittlung von Len(String)
;- Auf Feinheiten wie Überprüfung auf Verfügbarkeit und Sicherungen der Umgebung wurde verzichtet

Global X.l
Global Y.l
Global Len.l

Global String.s = "Ich bin ein fieser Teststaksjdfasdfkajslödfjaslkdjflöalöaksjdflkasjdlkfjasölkdfjlaksdjfing" 
 
 TestZeit.l  = ElapsedMilliseconds() 

 X = @String                 ;Zeiger auf den String

For i = 1 To 20000000 
 !mov ebx,[v_X]
 W0: 
 !movdqu xmm0,[ebx]          ;16 Byte des Strings einlesen
 !add ebx,16
 !pxor xmm1,xmm1             ;xmm1 auf Null setzen
 !pcmpeqb xmm0,xmm1          ;jedes der 16 Byte von xmm0 mit Null (xmm1) vergleichen 
 !cvtdq2ps xmm2,xmm0         ;Konvertierung von Integer in 4 Singles für nachfolgenden Vergleich
 !ucomiss xmm2,xmm1          ;vergleicht die 4 Singles mit Null  
 !jz l_w0                    ;Null-Byte (String-Ende) noch nicht gefunden

 !sub ebx,16                 ;Zeiger in String 16 Byte zurück und Ende suchen
 !mov edx,[v_X]
 !sub edx,ebx
 !jnc l_w1
 !sub ebx,12

W1:
 !mov eax,[ebx] 
 !test eax,0ffh 
 !jz l_w2
 !inc ebx
 !test eax,0ff00h
 !jz l_w2
 !inc ebx
 !test eax,0ff0000h
 !jz l_w2
 !inc ebx
 !test eax,0ff000000h
 !jz l_w2 
 !inc ebx 
 !jmp l_w1 
W2:
Next 

!mov [v_Y],ebx              ;"nur" für Anzeige der Stringlänge

Zeit = ElapsedMilliseconds() - TestZeit 

 !mov edx,[v_Y]
 !sub edx,[v_X]
 !mov [v_Len],edx     

MessageRequester("Len(String) mit SSE2","Stringlänge = " + Str(Len) + Chr(13) + "Testzeit = " + Str(Zeit) + " ms")

End
Gruss
Helle
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Beitrag von Helle »

Nachdem meine obigen Beispiele schamlos den Speicher-Verwaltungsmodus für Strings von PB ausgenutzt haben und mein SSE-Buch auch etwas unklar war, hier eine "saubere" Variante, die sogar noch etwas schneller ist.

Code: Alles auswählen

;- Helle 08.10.2006 PB4.00
;- Zeigt Einsatz von SSE zur Ermittlung von Len(String)
;- Auf Feinheiten wie Überprüfung auf Verfügbarkeit und Sicherungen der Umgebung wurde verzichtet

Global X.l
Global Y.l
Global Len.l

Global String.s = "Ich bin ein fieser Teststaksjdfasdfkajslödfjaslkdjflöalöaksjdflkasjdlkfjasölkdfjlaksdjfing" 
 
 TestZeit.l  = ElapsedMilliseconds() 

 X = @String                 ;Zeiger auf den String

For i = 1 To 20000000 
 !mov ebx,[v_X]
W0: 
 !movdqu xmm0,[ebx]          ;16 Byte des Strings einlesen
 !add ebx,16
 !pxor xmm1,xmm1             ;xmm1 auf Null setzen
 !pcmpeqb xmm0,xmm1          ;jedes der 16 Byte von xmm0 mit Null (xmm1) vergleichen und auf 0 oder 255 (bei Gleichheit) setzen
 !pmovmskb eax,xmm0          ;die Vorzeichen-Bits nach eax kopieren 
 !or ax,ax                   ;auf Null testen. Waren in xmm1 Null-Bytes, ist ax nicht mehr Null  
 !jz l_w0                    ;weiter einlesen, wenn Null-Byte (String-Ende) noch nicht gefunden

 !sub ebx,16                 ;Zeiger in String 16 Byte zurück und Ende suchen
W1:
 !mov eax,[ebx]              ;ab hier muss Byte-weise gesucht werden
 !test eax,0ffh 
 !jz l_w2
 !inc ebx
 !test eax,0ff00h
 !jz l_w2
 !inc ebx
 !test eax,0ff0000h
 !jz l_w2
 !inc ebx
 !test eax,0ff000000h
 !jz l_w2 
 !inc ebx 
 !jmp l_w1 
W2:
Next 

!mov [v_Y],ebx              ;"nur" für Anzeige der Stringlänge

Zeit = ElapsedMilliseconds() - TestZeit 

 !mov edx,[v_Y]
 !sub edx,[v_X]
 !mov [v_Len],edx    

MessageRequester("Len(String) mit SSE2","Stringlänge = " + Str(Len) + Chr(13) + "Testzeit = " + Str(Zeit) + " ms")

End
Gruss
Helle
Hellhound66
Beiträge: 476
Registriert: 23.03.2005 23:19

Beitrag von Hellhound66 »

Warum checkst du nicht die einzelnen Bits im EAX Register, dass ja schon das Ergebnis beinhaltet. Vielleicht so:

Code: Alles auswählen

@LOOP:
INC EBX
SHR EAX,1
JNC @LOOP
DEC EBX
Zuletzt geändert von Hellhound66 am 08.10.2006 23:56, insgesamt 1-mal geändert.
Optimismus ist ein Mangel an Information.
Antworten