FindString für 2 GB per ASM optimieren, ABER Syntax??

Für allgemeine Fragen zur Programmierung mit PureBasic.
hyperG
Beiträge: 23
Registriert: 28.06.2014 10:43

FindString für 2 GB per ASM optimieren, ABER Syntax??

Beitrag von hyperG »

Habe extrem große globale Speicherbereiche (1... 2GB) die ich nach Zeichenketten (8...16 Byte lang) durchsuche.
Zur Optimierung von FindString hat die Optimierungsphase 1:

Code: Alles auswählen

Procedure.i FindStringOptimierung1(strIn.s)
  Protected sString1.i, sString2.i, eString1.i, eString2.i , hString1.i,hString2.i,eEnd.i
  Protected *z1.Byte, *z2.Byte 
  sString1 = g_gepackteQuellGroesse+1
  sString2 = Len(strIn.s)+1
  hString1 = *g_mem ;Ptr auf 2 GB festen Speicher
  hString2 = @strIn
  If sString2 > sString1 Or sString2 = 0 
    ProcedureReturn 0 
  Else 
    eString1 = hString1 + sString1 - 1 
    eString2 = hString2 + sString2 - 1 
    eEnd = eString1 - sString2 +1
    For *z1 = hString1 To eEnd 
      *z2 = hString2 
      While *z1\b = *z2\b 
        *z1 + 1 
        *z2 + 1 
        If *z2 = eString2
          ProcedureReturn *z1 - sString2 - hString1 + 2 
        EndIf 
      Wend 
    Next 
  EndIf 
  ProcedureReturn 0
EndProcedure  
schon mal pro 20s eine Verbesserung um 1s und eine Halbierung des Speichers (String verbrauchte trotz ASCII und Compilerschalter "kein Unicode" immer 2 Bytes pro Zeichen)
gebracht.
Da hier viele gute ASM-Kenner mitlesen:
Wie kann man mit tollen ASM-Befehlen wie

Code: Alles auswählen

mov edi, *g_mem
mov eal,strIn.s
mov ecx,g_gepackteQuellGroesse
cld
repne scasb ; oder gleich 8 Bytes??
jnz quit ???
noch viel mehr herausholen (FindStringOptimierung2)??
Bitte nur 64 Bit, da ich den i7 voll auslasten möchte.
Oder noch schneller mit SSE4.2 Befehlen in Phase 3:

Code: Alles auswählen

; ecx = *g_mem, edx = @Suchstring
  push esi
  push edi
  MovDqU xmm2, dqword[edx] ;load 16 bytes Suchstring (was,wenn nur 8?)
  Pxor xmm3, xmm3
  lea eax, [ecx - 16]
  ; finde ersten 16-byte Fragmente in *g_mem
STRSTR_MAIN_LOOP:
    add eax, 16
    PcmpIstrI xmm2, dqword[eax], EQUAL_ORDERED
    ja STRSTR_MAIN_LOOP
  jnc STRSTR_NOT_FOUND
  add eax, ecx ; save mögliche Übereinstimmung start
  mov edi, edx
  mov esi, eax
  sub edi, esi
  sub esi, 16
  ; vergleiche die strings
@B:
    add esi, 16
    MovDqU    xmm1, dqword[esi + edi]
    ; mask out invalid bytes in the *g_mem
    PcmpIstrM xmm3, xmm1, EQUAL_EACH + NEGATIVE_POLARITY + BYTE_MASK
    MovDqU xmm4, dqword[esi]
    PAnd xmm4, xmm0
    PcmpIstrI xmm1, xmm4, EQUAL_EACH + NEGATIVE_POLARITY
    ja @B
  jnc STRSTR_FOUND
  ; continue next byte
  sub eax, 15
  jmp STRSTR_MAIN_LOOP
STRSTR_NOT_FOUND:
  xor eax, eax
STRSTR_FOUND:
  pop edi
  pop esi
  ret
Edit by NicTheQuick: Verschoben von "Windows" nach "Allgemein"
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: FindString für 2 GB per ASM optimieren, ABER Syntax??

Beitrag von NicTheQuick »

Ich blicke bei der Fragestellung noch nicht ganz durch.
Du hast einen riesigen String, der 2 GB Speicher benötigt und möchtest darin eine Zeichenkette suchen?

Willst du nur einmal eine Zeichenkette darin suchen oder gleich mehrmals verschiedene Zeichenketten im selben Riesenstring suchen? Falls letzteres der Fall sein sollte, dann böte sich ein Suffixtree an.

Ansonsten kann man in ASM sicherlich etwas optimieren, nur mangels Fachwissen kann ich nicht besonders weiter helfen.
Bild
hyperG
Beiträge: 23
Registriert: 28.06.2014 10:43

Re: FindString für 2 GB per ASM optimieren, ABER Syntax??

Beitrag von hyperG »

NicTheQuick hat geschrieben:...
Du hast einen riesigen String, der 2 GB Speicher benötigt und möchtest darin eine Zeichenkette suchen?
Willst du nur einmal eine Zeichenkette darin suchen oder gleich mehrmals verschiedene Zeichenketten im selben Riesenstring suchen? Falls letzteres der Fall sein sollte, dann böte sich ... an.
Ansonsten kann man in ASM sicherlich etwas optimieren, nur mangels Fachwissen kann ich nicht besonders weiter helfen.
Einerseits hast Du recht, es sind mehrere Suchstrings, aber diese Parallelisierung habe ich bereits per Multithreading optimiert.
Außerdem sind Quelle und Suchstrings Zufallszahlen -> da würde Speicher & Zeitverbrauch um vielfaches Größer, als die Quelle selbst!

Ich suche wirklich analog zu FindString nur das erste Vorkommen (Position) des Suchstrings - nur schneller.
Ich habe hier schon tolle ASM-Code gesehen -> bin optimistisch.

(komme von VisualStudioExpress2012:
- funktioniert seit update nicht mehr unter Win7 !
- ständig versucht eine exe nach Hause zu telefonieren
- die erzeugte exe ist oft viel zu groß ... was packen die da noch alles rein ...
- Dialoge nur mit c# {langsamer Interpreter statt schneller Compiler})

PureBasic scheint genau das richtige zu sein, wenn man ASM richtig einsetzen kann...
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: FindString für 2 GB per ASM optimieren, ABER Syntax??

Beitrag von NicTheQuick »

Die einfachste Nicht-ASM-Methode ist jedenfalls diese hier.

Code: Alles auswählen

Procedure findMemory(*pString.Character, stringToFind.s)
	Protected *pFind.Character = @stringToFind
	Protected *start = *pString
	
	If (*pFind\c = 0)
		ProcedureReturn 1
	EndIf
	
	While *pString\c
		If (*pFind\c = *pString\c)
			*pFind + SizeOf(Character)
			*pString + SizeOf(Character)
			If (*pFind\c = 0)
				Break
			EndIf
		Else
			*pString + SizeOf(Character)
			*pFind = @stringToFind
		EndIf
	Wend
	
	If (*pFind\c = 0)
		ProcedureReturn 1 + (*pString - (*pFind - @stringToFind) - *start) / SizeOf(Character)
	Else
		ProcedureReturn 0
	EndIf
EndProcedure

s.s = "Das ist ein mächtiger langer String, der ganz viel dummes Zeugs im Kopf hat."

Debug FindString(s, "s Z")
Debug findMemory(@s, "s Z")
Aber optimiert ist da natürlich auch noch nichts. Und das sollte man zuerst machen. Man sollte zuerst den eiffzientesten Algorithmus suchen, mit dem man Strings durchsuchen kann. Und dann kann man mit ASM weiter machen. Mit ASM kann man schließlich auch langsam programmieren, wenn man es nicht richtig macht.

Darf ich fragen, was das Grundproblem ist, was du lösen willst?

Edit:
Vielleicht hilft dir das hier noch: Wiki - String-Matching-Algorithmus
Bild
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: FindString für 2 GB per ASM optimieren, ABER Syntax??

Beitrag von Helle »

Vor Jahren hatte ich diesen Code für das englische Forum vorbereitet:

Code: Alles auswählen

Procedure.q FindString_SSE42_64Bit(pStr1, pStr2, StartPos)
  !mov rax,r8                               ;need RAX for return-value ProcedureReturn, R8=StartPos
  !cmp rax,1
  !jge @f
  !mov rax,1                                ;or End or Error
!@@:
  !mov r8,rcx                               ;need RCX for return-value PCMPISTRI, RCX=pStr1 
  !sub r8,1
  !add r8,rax                               ;rax=StartPos
  !movdqu xmm0,[rdx]                        ;first max.16 Bytes of Find$, RDX=pStr2
  !xor rcx,rcx
!Search:
  !add rax,rcx
  !add r8,rcx
  !pcmpistri xmm0,dqword[r8],00001100b      ;Least Significant Index, Positive Polarity (IntRes2=IntRes1), Equal Ordered, Unsigned Bytes (=No Unicode)  
  !jz Base$End                              ;Zero-Flag is set if found end of Base$
  !js Find$End                              ;Sign-Flag is set if found end of Find$  
  !jrcxz @f                                 ;jump if RCX is zero 
  !jmp Search
!@@:
  !movdqa xmm1,xmm0                         ;save xmm0
  !mov r9,r8
  !mov r10,rdx
!@@:
  !add r10,16
  !add r9,16                                ;next 16 Bytes of Base$
  !movdqu xmm1,[r10]                        ;next 16 Bytes of Find$
  !pcmpistri xmm1,dqword[r9],00001100b      ;compare the strings 
  !jz Base$End
  !js Find$End
  !jrcxz @b
  !jmp Search
!Base$End:
  !cmp rcx,16
  !jne Found
  !xor rax,rax
  !jmp NoFound
!Find$End:
  !or rcx,rcx                               ;RCX Zero? Conventional
  !jnz Search
!Found:
  !add rax,rcx
!NoFound:
 ProcedureReturn                            ;rax 
EndProcedure 

Base$ = "This is a simple string for testing several find procedures ~ the results will show, if any of the tested functions are faster than the internal FindString() function"
Find$ = "Find"

Pos = 1

FindSSE42 = FindString_SSE42_64Bit(@Base$, @Find$, Pos)
SSE42$ = "Found String at Position : " + Str(FindSSE42)

PB$ = "Test with PB : " + Str(FindString(Base$, Find$, Pos))

MessageRequester("FindString with SSE4.2", SSE42$ + #LFCR$ + PB$)
Die Strings sollten 16-Alignment haben und "Luft" nach hinten; halbwegs aktuelle PB-Versionen garantieren dies. Zur Sicherheit habe ich trotzdem "movdqu" verwendet. Ansonsten mach dir ´nen Kopp :mrgreen: !
Gruß
Helle
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: FindString für 2 GB per ASM optimieren, ABER Syntax??

Beitrag von NicTheQuick »

Ist das jetzt der naive Algorithmus? Denn der Knuth-Morris-Pratt-Algorithmus oder ein Suffixbaum sind nach wie vor schneller. Sie brauchen nur O(m + n) Zeit.
Bild
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: FindString für 2 GB per ASM optimieren, ABER Syntax??

Beitrag von NicTheQuick »

Okay, bisher sehe ich noch keinen Vorteil bei Knuth-Morris-Pratt. Vielleicht nutzt aber PB den ja auch? Wer weiß. :D

Code: Alles auswählen

; Knuth-Morris-Prat Implementierung von hier:
;     http://www-igm.univ-mlv.fr/~lecroq/string/node8.html
; Siehe auch:
;     http://de.wikipedia.org/wiki/Knuth-Morris-Pratt-Algorithmus

EnableExplicit

Structure cArray
	c.c[0]
EndStructure

Procedure preKmp(*x.cArray, m.i, Array N.i(1))
	Protected.i i = 0, j = -1
	N(0) = -1
	
	While (i < m)
		While (j >= 0 And *x\c[i] <> *x\c[j])
			j = N(j)
		Wend
		i + 1
		j + 1
		;If (*x\c[i] = *x\c[j])
		;	N(i) = N(j)
		;Else
			N(i) = j
		;EndIf
	Wend
EndProcedure

Procedure.i KMP(*x.cArray, m.i, *y.cArray)
	Protected.i i = 0, j = 0
	Protected Dim N.i(m + 1)
	
	preKmp(*x, m, N())
	For i = 0 To m
		Debug N(i)
	Next
	i = 0
	
	While (*y\c[i])
		While (j >= 0 And *y\c[i] <> *x\c[j])
			j = N(j)
		Wend
		i + 1
		j + 1
		If (*x\c[j] = 0)
			ProcedureReturn i - m + 1
			j = N(j)
		EndIf
	Wend
	
	ProcedureReturn 0
EndProcedure

Define Base.s, Find.s, Result.s

Base = "This is a simple string for testing several find procedures ~ the results will show, if any of the tested functions are faster than the internal FindString() function"
Base + Base
Base + Base
Base + Base
Base + Base
Base + Base
Base + Base
Base + Base
Base + Base
Base + Base
Base + Base
Base + Base
Base + Base
Base + Base
Base + "FindString[] bliblablubb Hier ist Ende Gelände." 
Find = "FindString[]"

MessageRequester("Info", "Suchstring hat " + Str(Len(Find)) + " Zeichen. Base hat " + Str(Len(Base)) + " Zeichen.")

Define Pos.i, time.i

time = ElapsedMilliseconds()
Pos = KMP(@Find, Len(Find), @Base)
time = ElapsedMilliseconds() - time

Result = "KMP: @" + Pos + " in " + time + " ms." + #LF$

time = ElapsedMilliseconds()
Pos = FindString(Base, Find)
time = ElapsedMilliseconds() - time

Result + "PB: @" + Pos + " in " + time + " ms." + #LF$

MessageRequester("FindString", Result)
Bild
hyperG
Beiträge: 23
Registriert: 28.06.2014 10:43

Re: FindString für 2 GB per ASM optimieren, ABER Syntax??

Beitrag von hyperG »

Super!! 3,3 mal schneller! auf 10 GB!! bei 28 Suchstrings!!

Danke Helle!!!
hyperG
Beiträge: 23
Registriert: 28.06.2014 10:43

Re: FindString für 2 GB per ASM optimieren, ABER Syntax??

Beitrag von hyperG »

Ich möchte mich hiermit nochmals bei allen Antwortern bedanken.
Die Zusammenfassung und der enorme Nutzen (optimiertes PureBasic 1 Stunde Sucharbeit gegenüber mehr als 2 Tage bei "normaler Software"!)
können unter http://www.gerdlamprecht.de/BisZuWelche ... gKombi.htm
nachgelesen werden.

Gruß hyperG
:wink:
:D
Antworten