AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Schnellster Stringmatching-Algorithmus in ASM übersetzen
Thema durchsuchen
Ansicht
Themen-Optionen

Schnellster Stringmatching-Algorithmus in ASM übersetzen

Offene Frage von "Sereby"
Ein Thema von alzaimar · begonnen am 5. Dez 2007 · letzter Beitrag vom 3. Jul 2008
Antwort Antwort
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#1

Re: Schnellster Stringmatching-Algorithmus in ASM übersetzen

  Alt 8. Dez 2007, 19:08
Hi Amateurprofi. Natürlich ist meine Testumgebung sicher. Ich erzeuge einen Zufallsstring mit dem Alphabet X. Der Suchstring enthält Zeichen, die nicht in X sind.

Ich finde 7% auch sehr gut und daher wird deine Version -mit deiner Erlaubnis- in meine Unit übernommen.

Hier der FastPos-Code für Alle, bei denen der Download nicht klappt.

Delphi-Quellcode:
(******************************************************************************
* FastPos-Unit. Zusammenarbeit der Delphi-Praxis, FastCode-Challenge und    *
*  dem QuickSearch-Algorithmus von Daniel Sunday                            *
*                                                                            *
* Stellt eine Funktion zum Suchen eines Teilstrings in einem String zur      *
* Verfügung. Dabei wird, in Abhängigkeit der Stringlängen der jeweils beste  *
* Algorithmus ausgewählt                                                    *
* Wenn der Suchstring aus einem Zeichen besteht, wird der Gewinner der      *
* FastCode-Challenge 'CharPos' ([url]www.fastcode.org[/url]) aufgerufen                *
*                                                                            *
* Wenn der Suchstring aus 2 oder 3 Zeichen besteht, oder der zu durchsuchende*
* Text relativ kurz ist, dann wird der Gewinner der FastCode-Challenge 'Pos' *
* aufgerufen.                                                                *
* In allen anderen Fällen wird ein modifizierter QuickSearch-Algorithmus    *
* verwendet, der mit einer Optimierung von Timo Raita arbeitet.              *
*                                                                            *
******************************************************************************)

Unit FastPosUnit;

Interface
Function DPPos(Const sSubStr, sString: String): Integer;
Implementation

Uses
  SysUtils;

Function QPosEx(SearchFor, SearchIn: String; Start: integer): integer;
Asm
               pushad // Alle Register auf Stack legen
          sub esp,$400 // Platz für Skiptabelle schaffen
               // Auf dem Stack liegen folgende Daten
               // ESP : Skiptabelle
               // ESP+$400..$410 : EDI, ESI, EBP, ESP, EBX
               // ESP+$414 : EDX Adresse SearchIn
               // ESP+$418 : ECX Start
               // ESP+$41C : EAX Adresse SearchFor
               // ESP+$420 : EBP
          // ESP+$424 : Returnadresse

               // Prüfen, ob SearchFor oder SearchIn leer ist, oder Start negativ ist
               test eax,eax
               je @Fail // SearchFor ist leer
               test edx,edx
               je @Fail // Searchin ist leer
               mov ebx,[eax-4] // Länge SearchFor
               test ebx,ebx
               je @Fail // SearchFor ist leer
               test ecx,ecx
               js @Fail // Start ist negative

               // Erste und letzte mögliche Fundstelle in ESI bzw. EBP
               je @1
               sub ecx,1 // =Start 0-basierend
@1: lea esi,[edx+ecx] // Ab hier soll gesucht werden
               add edx,[edx-4]
               mov ebp,edx
               sub ebp,ebx // Bis hier soll gesucht werden

               // Prüfen, ob Erste mögliche Fundstelle hinter letzter liegt
               cmp esi,ebp
               ja @Fail

               // Prüfen, ob SearchFor nur 1 Zeichen ist
               // Dann ist keine SkipTabelle erforderlich
          cmp ebx,1
               je @Search1


               // Skiptabelle erstellen
               // 1) for i:=0 to 255 do Skip[i]:=n+1
               mov edi,esp
               mov ecx,$100
               mov eax,ebx
               add eax,1 // Length(Searchfor)+1
               rep stosd
               // 2) For i:=0 To n-1 Do Skip[SearchFor[i]]:=n-i
               mov edi,[esp+$41C] // Adresse SearchFor
               mov eax,ebx // Length(Searchfor)
@FillSkip: movzx edx,[edi+ecx] // SearchFor[i]
               mov [esp+edx*4],eax // Skip[SearchFor[i]]:=n-i
               add ecx,1
               sub eax,1
               jne @FillSkip

               // Erstes und Letztes Zeichen von SearchFor in AL/AH
               mov al,[edi] // Erstes Zeichen SearchFor
               mov ah,[edi+ebx-1] // Letzes Zeichen SearchFor

               // Prüfen, ob Length(SearchFor) > 6 ist
               cmp ebx,6
               ja @SearchX
          jmp @Vector.Pointer[ebx*4-8]
@Vector: dd @Search2
               dd @Search3
               dd @Search4
               dd @Search4 // Länge=5
               dd @Search6

// Länge Searchfor > 6
@SearchX: add edi,1 // Auf zweites Zeichen von SearchFor
               mov [esp+$41C],edi // Merken für CompareMem
               jmp @Search
// Länge SearchFor > 6
@SkipX: mov esi,edx // Aufruf ex CompareMem
@Skip: movzx edx,[esi+ebx]
          add esi,[esp+edx*4]
               cmp esi,ebp
               ja @Fail
@Search: cmp [esi],al
               jne @Skip
               cmp [esi+ebx-1],ah
               jne @Skip
               // Zweites bis vorletzten Zeichen von SearchFor prüfen
               mov edx,esi // ESI retten
               mov edi,[esp+$41C] // Zeiger zweites Zeichen von SearchFor
               add esi,1 // Zeiger Text auf nächstes Zeichen
               mov ecx,ebx // Length(SearchFor)
               sub ecx,2 // -2 (Erstes und letztes Zeichen)
          shr ecx,2 // =Anzahl DWords
               repe cmpsd // DWords vergleichen
               jne @SkipX // Nicht gleich
               mov ecx,ebx // Length(SearchFor)
               sub ecx,2 // -2 (Erstes und letztes Zeichen)
               and ecx,3 // =Anzahl restliche Bytes
               repe cmpsb // Bytes vergleichen
               jne @SkipX // Nicht gleich
               mov esi,edx // Fundstelle
               jmp @Found

// Länge SearchFor=1
@Search1: mov ecx,ebp // Letzte mögliche Fundstelle
               sub ecx,esi // - Erste mögliche Fundstelle
          add ecx,1 // = Anzahl zu prüfende Zeichen
               neg ecx
               sub esi,ecx
               mov al,[eax] // zu suchendes Zeichen
@Loop: cmp al,[esi+ecx]
               je @Found1
               add ecx,1
               jne @Loop
               jmp @Fail
@Found1: lea esi,[esi+ecx] // ESI auf Fundstelle
               jmp @Found

// Länge SearchFor=2
@Skip2: movzx edx,[esi+ebx]
               add esi,[esp+edx*4]
               cmp esi,ebp
               ja @Fail
@Search2: cmp [esi],al
               jne @Skip2
               cmp [esi+ebx-1],ah
               jne @Skip2
               jmp @Found

// Länge SearchFor=3
@Skip3: movzx edx,[esi+ebx]
               add esi,[esp+edx*4]
               cmp esi,ebp
          ja @Fail
@Search3: cmp [esi],al
               jne @Skip3
               cmp [esi+ebx-1],ah
               jne @Skip3
               mov dx,[edi]
               cmp dx,[esi]
               jne @Skip3
               jmp @Found

// Länge SearchFor=4 oder 5
@Skip4: movzx edx,[esi+ebx]
               add esi,[esp+edx*4]
          cmp esi,ebp
               ja @Fail
@Search4: cmp [esi],al
               jne @Skip4
               cmp [esi+ebx-1],ah
               jne @Skip4
               mov edx,[edi]
               cmp edx,[esi]
               jne @Skip4
               jmp @Found

// Länge SearchFor=6
@Skip6: movzx edx,[esi+ebx]
               add esi,[esp+edx*4]
          cmp esi,ebp
               ja @Fail
@Search6: cmp [esi],al
               jne @Skip6
               cmp [esi+ebx-1],ah
               jne @Skip6
               mov edx,[edi+1]
               cmp edx,[esi+1]
               jne @Skip6
               jmp @Found

@Found: // Gefunden, Index berechnen
               sub esi,[esp+$414]
          add esi,1
               jmp @SetRes

@Fail: // Nicht gefunden, Result=0
               xor esi,esi

@SetRes: // Result speichern
               mov [esp+$41C],esi

               // Skiptabelle vom Stack nehmen
               add esp,$400

               // Register wieder herstellen, Result in EAX
               popad
End;

Function CharPos_JOH_SSE2_1_b(Ch: Char; Const Str: AnsiString): Integer;
Asm
  test edx, edx
  jz @@NullString
  mov ecx, [edx-4]
  push ebx
  mov ebx, eax
  cmp ecx, 16
  jl @@Small
@@NotSmall:
  mov ah, al {Fill each Byte of XMM1 with AL}
  movd xmm1, eax
  pshuflw xmm1, xmm1, 0
  pshufd xmm1, xmm1, 0
@@First16:
  movups xmm0, [edx] {Unaligned}
  pcmpeqb xmm0, xmm1 {Compare First 16 Characters}
  pmovmskb eax, xmm0
  test eax, eax
  jnz @@FoundStart {Exit on any Match}
  cmp ecx, 32
  jl @@Medium {If Length(Str) < 32, Check Remainder}
@@Align:
  sub ecx, 16 {Align Block Reads}
  push ecx
  mov eax, edx
  neg eax
  and eax, 15
  add edx, ecx
  neg ecx
  add ecx, eax
@@Loop:
  movaps xmm0, [edx+ecx] {Aligned}
  pcmpeqb xmm0, xmm1 {Compare Next 16 Characters}
  pmovmskb eax, xmm0
  test eax, eax
  jnz @@Found {Exit on any Match}
  add ecx, 16
  jle @@Loop
  pop eax {Check Remaining Characters}
  add edx, 16
  add eax, ecx {Count from Last Loop End Position}
  jmp dword ptr [@@JumpTable2-ecx*4]
  nop
  nop
@@NullString:
  xor eax, eax {Result = 0}
  ret
  nop
@@FoundStart:
  bsf eax, eax {Get Set Bit}
  pop ebx
  inc eax {Set Result}
  ret
  nop
  nop
@@Found:
  pop edx
  bsf eax, eax {Get Set Bit}
  add edx, ecx
  pop ebx
  lea eax, [eax+edx+1] {Set Result}
  ret
@@Medium:
  add edx, ecx {End of String}
  mov eax, 16 {Count from 16}
  jmp dword ptr [@@JumpTable1-64-ecx*4]
  nop
  nop
@@Small:
  add edx, ecx {End of String}
  xor eax, eax {Count from 0}
  jmp dword ptr [@@JumpTable1-ecx*4]
  nop
@@JumpTable1:
  dd @@NotFound, @@01, @@02, @@03, @@04, @@05, @@06, @@07
  dd @@08, @@09, @@10, @@11, @@12, @@13, @@14, @@15, @@16
@@JumpTable2:
  dd @@16, @@15, @@14, @@13, @@12, @@11, @@10, @@09, @@08
  dd @@07, @@06, @@05, @@04, @@03, @@02, @@01, @@NotFound
@@16:
  add eax, 1
  cmp bl, [edx-16]
  je @@Done
@@15:
  add eax, 1
  cmp bl, [edx-15]
  je @@Done
@@14:
  add eax, 1
  cmp bl, [edx-14]
  je @@Done
@@13:
  add eax, 1
  cmp bl, [edx-13]
  je @@Done
@@12:
  add eax, 1
  cmp bl, [edx-12]
  je @@Done
@@11:
  add eax, 1
  cmp bl, [edx-11]
  je @@Done
@@10:
  add eax, 1
  cmp bl, [edx-10]
  je @@Done
@@09:
  add eax, 1
  cmp bl, [edx-9]
  je @@Done
@@08:
  add eax, 1
  cmp bl, [edx-8]
  je @@Done
@@07:
  add eax, 1
  cmp bl, [edx-7]
  je @@Done
@@06:
  add eax, 1
  cmp bl, [edx-6]
  je @@Done
@@05:
  add eax, 1
  cmp bl, [edx-5]
  je @@Done
@@04:
  add eax, 1
  cmp bl, [edx-4]
  je @@Done
@@03:
  add eax, 1
  cmp bl, [edx-3]
  je @@Done
@@02:
  add eax, 1
  cmp bl, [edx-2]
  je @@Done
@@01:
  add eax, 1
  cmp bl, [edx-1]
  je @@Done
@@NotFound:
  xor eax, eax
  pop ebx
  ret
@@Done:
  pop ebx
End;

Procedure Filler2;
Asm
 nop
End;

Function Pos_JOH_IA32_6(Const SubStr: AnsiString; Const Str: AnsiString): Integer;
Asm {Slightly Cut-Down version of PosEx_JOH_6}
  push ebx
  cmp eax, 1
  sbb ebx, ebx {-1 if SubStr = '' else 0}
  sub edx, 1 {-1 if S = ''}
  sbb ebx, 0 {Negative if S = '' or SubStr = '' else 0}
  jl @@InvalidInput
  push edi
  push esi
  push ebp
  push edx
  mov edi, [eax-4] {Length(SubStr)}
  mov esi, [edx-3] {Length(S)}
  cmp edi, esi
  jg @@NotFound {Offset to High for a Match}
  test edi, edi
  jz @@NotFound {Length(SubStr = 0)}
  lea ebp, [eax+edi] {Last Character Position in SubStr + 1}
  add esi, edx {Last Character Position in S}
  movzx eax, [ebp-1] {Last Character of SubStr}
  add edx, edi {Search Start Position in S for Last Character}
  mov ah, al
  neg edi {-Length(SubStr)}
  mov ecx, eax
  shl eax, 16
  or ecx, eax {All 4 Bytes = Last Character of SubStr}
@@MainLoop:
  add edx, 4
  cmp edx, esi
  ja @@Remainder {1 to 4 Positions Remaining}
  mov eax, [edx-4] {Check Next 4 Bytes of S}
  xor eax, ecx {Zero Byte at each Matching Position}
  lea ebx, [eax-$01010101]
  not eax
  and eax, ebx
  and eax, $80808080 {Set Byte to $80 at each Match Position else $00}
  jz @@MainLoop {Loop Until any Match on Last Character Found}
  bsf eax, eax {Find First Match Bit}
  shr eax, 3 {Byte Offset of First Match (0..3)}
  lea edx, [eax+edx-3] {Address of First Match on Last Character + 1}
@@Compare:
  cmp edi, -4
  jle @@Large {Lenght(SubStr) >= 4}
  cmp edi, -1
  je @@SetResult {Exit with Match if Lenght(SubStr) = 1}
  movzx eax, word ptr [ebp+edi] {Last Char Matches - Compare First 2 Chars}
  cmp ax, [edx+edi]
  jne @@MainLoop {No Match on First 2 Characters}
@@SetResult: {Full Match}
  lea eax, [edx+edi] {Calculate and Return Result}
  pop edx
  pop ebp
  pop esi
  pop edi
  pop ebx
  sub eax, edx {Subtract Start Position}
  ret
@@NotFound:
  pop edx {Dump Start Position}
  pop ebp
  pop esi
  pop edi
@@InvalidInput:
  pop ebx
  xor eax, eax {No Match Found - Return 0}
  ret
@@Remainder: {Check Last 1 to 4 Characters}
  mov eax, [esi-3] {Last 4 Characters of S - May include Length Bytes}
  xor eax, ecx {Zero Byte at each Matching Position}
  lea ebx, [eax-$01010101]
  not eax
  and eax, ebx
  and eax, $80808080 {Set Byte to $80 at each Match Position else $00}
  jz @@NotFound {No Match Possible}
  lea eax, [edx-4] {Check Valid Match Positions}
  cmp cl, [eax]
  lea edx, [eax+1]
  je @@Compare
  cmp edx, esi
  ja @@NotFound
  lea edx, [eax+2]
  cmp cl, [eax+1]
  je @@Compare
  cmp edx, esi
  ja @@NotFound
  lea edx, [eax+3]
  cmp cl, [eax+2]
  je @@Compare
  cmp edx, esi
  ja @@NotFound
  lea edx, [eax+4]
  jmp @@Compare
@@Large:
  mov eax, [ebp-4] {Compare Last 4 Characters of S and SubStr}
  cmp eax, [edx-4]
  jne @@MainLoop {No Match on Last 4 Characters}
  mov ebx, edi
@@CompareLoop: {Compare Remaining Characters}
  add ebx, 4 {Compare 4 Characters per Loop}
  jge @@SetResult {All Characters Matched}
  mov eax, [ebp+ebx-4]
  cmp eax, [edx+ebx-4]
  je @@CompareLoop {Match on Next 4 Characters}
  jmp @@MainLoop {No Match}
End;

Function DPPos(Const sSubStr, sString: String): Integer;
Var
  i: Integer;
  c: Char;
Begin
  Case Length(sSubStr) Of
    0:
      Result := 0;
    1:
      Result := CharPos_JOH_SSE2_1_b(sSubStr[1], sString);
    2, 3:
      Result := Pos_JOH_IA32_6(sSubStr, sString);
  Else
    If Length(sString) < 1000 Then // 1000 is ne Hausnummer
      Result := Pos_JOH_IA32_6(sSubStr, sString)
    Else
      Result := QPosEx(sSubStr, sString,1); // Dann kann man auf das '(n<3) or' verzichten!
  End
End;
End.
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:22 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz