Seite 1 von 3

Schneller Len(String$) Befehl

Verfasst: 06.10.2006 22:57
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.

Verfasst: 07.10.2006 02:38
von ts-soft
Bei Unicode ergibts immer 1
Das machen die langsameren besser :wink:

Verfasst: 07.10.2006 03:08
von edel

Code: Alles auswählen

CompilerIf #PB_Compiler_Unicode
CompilerElse
  Macro Len(string)
    HellLen(string)
  EndMacro 
CompilerEndIf
Wer braucht schon unicode ... :lol:

Verfasst: 07.10.2006 13:53
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

Verfasst: 07.10.2006 14:26
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

Verfasst: 07.10.2006 14:27
von remi_meier
Nimm einfach mehr Durchläufe. Die Zeiten sollten grösser als 1 Sek. sein.

Verfasst: 07.10.2006 21:52
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

Verfasst: 08.10.2006 10:04
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

Verfasst: 08.10.2006 22:16
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

Verfasst: 08.10.2006 22:31
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