Autor Beitrag
Heiko
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: So 12.10.08 18:45 
Hallo,

ich hab mal wieder eine Unit vom mir angefasst um diese zu optimieren. Dabei soll im String überprüft werden, wo Zeilenumbrüche sind. Doch leider findet repne nichts :(. Zum testen habe ich in eine Datei drei Zeilen á drei Buchstaben getippt. repne liefert aber die Position 9 zurück - also schon hinter dem String. Sieht ewiner von euch den Fehler?

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
function CharPos(const PStr: PChar; Chr: Char; StrLen: Integer): Integer;
begin
  asm
    push edi
    mov edi, PStr           // In PStr suchen
    mov ecx, StrLen         // Max. Stringlänge suchen
    mov al, Chr             // Chr suchen
    cld                     // Vorwärts
    repne scasb             // Byteweise nach al suchen
    mov Result, ecx
    pop edi
  end;
end;

function TFileStreamW.ReadLn(var Buffer: String): Boolean;
const
  bufMaxSize = 4096;
var
  start      : integer;
  bufSize    : integer;
  chrPos     : integer;
  newPos     : integer;

{*var StartPos, BufPos, ReadBytes: Cardinal;
    CRPos: Integer;*}


begin
  SetLength(Buffer, bufMaxSize);
  start       := position;

  chrPos := 1;
  bufSize := Read(Buffer[chrPos], bufMaxSize);
  while(bufSize<>0do
  begin
    newPos  := CharPos(@Buffer[chrPos], #13, bufSize);
    if newPos = bufSize then
    begin
      newPos  := CharPos(@Buffer[chrPos], #10, bufSize);
    end;
    if newPos < bufSize then
    begin
      SetLength(Buffer, chrPos+newPos);
      Position:=start+chrPos+newPos+1;
    end
    else
    begin
      SetLength(Buffer, length(Buffer)+bufMaxSize);
      bufSize := Read(Buffer[chrPos], bufMaxSize);
      inc(chrPos, bufMaxSize);
    end;
  end;
cytrinox
Hält's aus hier
Beiträge: 1



BeitragVerfasst: So 12.10.08 19:05 
Hi,

ich könnte mich irren, aber rein von der Logik her hätte ich gesagt scasb such im esi (source index) und nicht edi (destination index) Register.

Edit: gut, habs grad nachgelesen - ich hab mich geirrt ;)
Marc.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1876
Erhaltene Danke: 129

Win 8.1, Xubuntu 15.10

BeitragVerfasst: So 12.10.08 20:12 
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
function CharPos(const PStr: PChar; Chr: Char; StrLen: Integer): Integer;
asm
    cld                
    push edi
    mov edi, PStr           
    mov ecx, StrLen        
    mov eax, ecx
    push eax
    mov al, Chr             
    repnz scasb
    pop eax      
    sub eax, ecx
    mov Result, eax
    pop edi
end;


Hi! Ersetze das repne durch ein repnz.
Beachte, dass von hinten gezählt wird, beginnend bei Null! Bei meiner Variante bereits korrigiert.

€1: So scheint es bei mir zu funktionieren, allerdings auch mit einem repne.
€2: Bei mir funktioniert nun plötzlich auch Deine ursprüngliche Funktion. Bin etwas confused. :gruebel:
€3: Hat es einen Grund, weshalb Du das EDI - Register sicherst?
€4: Hast Du vielleicht vergessen, dass Zeilenumbrüche als zwei Zeichen gezählt werden? (#10#13)
€5: Codeupdate, ist nun performanter.


Zuletzt bearbeitet von Marc. am So 12.10.08 23:41, insgesamt 4-mal bearbeitet
Silas
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 478

Windows XP Home
Delphi 2005, RAD Studio 2007, MASM32, FASM, SharpDevelop 3.0
BeitragVerfasst: So 12.10.08 20:55 
N'Abend,

zuerst: repne == repnz weil *z immer == *e ist

Bei rep* kommt eigentlich immer Position+1 heraus, weil er nach dem Fund abbricht (ohne Gewähr).
Ein cld kann nie schaden, sonst wird u.U. rückwärts gesucht (Direction Flag).

EBX muss immer gesichert werden (natürlich nur wenns geändert wird), EDI und ESI nur bei Klassenfunktionen (hat mir user profile iconBenBE mal irgendwann erklärt), also in diesem Fall nicht unbedingt.

Komisch, ich wäre jetzt auch zu 95�icher gewesen, dass es bei scas* ESI ist.

_________________
Religionskriege sind nur Streitigkeiten darüber, wer den cooleren imaginären Freund hat ;-)
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mo 13.10.08 00:13 
Nana ...nicht mir meine Erklärungen verdrehen ;-)

user profile iconSilas hat folgendes geschrieben Zum zitierten Posting springen:
EBX muss immer gesichert werden (natürlich nur wenns geändert wird), EDI und ESI nur bei Klassenfunktionen (hat mir user profile iconBenBE mal irgendwann erklärt), also in diesem Fall nicht unbedingt.


Wo ich Dir das erklärt habe, habe ich gesagt: in EDI werden bei Klassen-Funktionen die RTTI\Instanztyp-Daten übergeben, bzw. der DMI bei Dynamic-Methoden. Nicht sichern muss man nur EAX, EDX und ECX; alle anderen Register sind IMMER zu sichern, wenn diese verändert werden, da dies der Compiler von selbst nicht tut (und er sogar davon ausgeht, dass fremde Methoden da nicht dran rumspielen)!

Bzgl. CLD vielleicht mal noch ne Anmerkung: Sollte man immer mit setzen, selbst wenn der Default-Wert Cleared ist. Es gibt einige Bugs (Buffer Overruns), die darauf beruhen, dass das Direction-Flag in einem unerwarteten Zustand vorgefunden wird. Um aber für das Setzen des Direction-Flags nicht unnötig Zeit zu verschwenden, kann man diess in seiner Methodee soweit keine Verzweigungen vorhanden sind, nach vorne verlagern, und es an eine Stelle auslagern, wo die entsprechende Pipeline des Prozessors gerade frei ist. Anbieten tun sich dafür Befehle, die das Flag-Register nicht verändern (z.B. mov r32,r32).

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: Mo 13.10.08 01:15 
Hi,

mal eine ganz doofe Frage am Rande: Bringt es überhaupt was das in Assembler zu programmieren?
Wie sieht der von Delphi generierte Code aus?
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mo 13.10.08 01:22 
user profile iconThorsten83 hat folgendes geschrieben Zum zitierten Posting springen:
Hi,

mal eine ganz doofe Frage am Rande: Bringt es überhaupt was das in Assembler zu programmieren?

Wenn man weiß, was man tut: JA!

user profile iconThorsten83 hat folgendes geschrieben Zum zitierten Posting springen:
Wie sieht der von Delphi generierte Code aus?

Meistens grausam und redundant.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: Mo 13.10.08 01:55 
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
    pushl  %ebp
  movl  %esp, %ebp
  movl  16(%ebp), %ecx
  pushl  %ebx
  movzbl  12(%ebp), %edx
  movl  8(%ebp), %ebx
  testl  %ecx, %ecx
  jle  .L2
  xorl  %eax, %eax
  cmpb  %dl, (%ebx)
  jne  .L7
  jmp  .L6
  .p2align 4,,7
.L8:
  cmpb  %dl, (%eax,%ebx)
  .p2align 4,,5
  je  .L6
.L7:
  addl  $1, %eax
  cmpl  %ecx, %eax
  .p2align 4,,5
  jne  .L8
.L2:
  movl  $-1, %eax
.L6:
  popl  %ebx
  popl  %ebp
  .p2align 4,,1
  ret


Ist zugegebenermaßen vom gcc, aber ich finde der Code sieht noch einigermaßen aus...
Und ist letztendlich auch nicht viel anders als das selbst geschriebene, oder ist die Variante mit repne schneller?
Heiko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Mo 13.10.08 11:23 
Da hier scheinbar kein Fehler zu finden ist, hänge ich mal das Projekt an. Vlt. sieht einer da etwas?

user profile iconMarc. hat folgendes geschrieben Zum zitierten Posting springen:
€4: Hast Du vielleicht vergessen, dass Zeilenumbrüche als zwei Zeichen gezählt werden? (#10#13)

Nein. Das kommt noch. Aber ich teste lieber zwischenstände als später einen größeren Block, der nicht mehr so übersichtlig ist ;).

@Silas: Der Code dürfte dir bekannt Vorkommen - wobei ich den abgespeckt habe (unnötige Berechnungen entfernt).

@BenBE: Kennst du eine alternative zu repne, die nach #10 & #13 (& #21) gleichzeitig sucht?

user profile iconThorsten83 hat folgendes geschrieben Zum zitierten Posting springen:
Und ist letztendlich auch nicht viel anders als das selbst geschriebene, oder ist die Variante mit repne schneller?

Das Teste ich auch mit diesem Code. Das Borland-readln benutzt eine eigenen Code dafür (such "parallel" nach #10, #13, #21). Mein Ziel ist es: Borland performancetechnisch zu überbeiten ;).
Einloggen, um Attachments anzusehen!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 13.10.08 12:24 
Ganz einfach: ecx wird ja heruntergezählt bei der Schleife, und sobald das 0 ist (Stringende) oder das Zeroflag gesetzt ist wird abgebrochen bei repne.
Du musst nur das Ergebnis von der Länge abziehen ;-). 13 - 9 = 4 ;-).

Was den Abbruch verursacht hat musst du dann glaube ich auch noch unterscheiden. (Am Zeroflag)

// EDIT:
Wobei das bei dir aber auch egal ist, warum es abgebrochen hat, wenn ich es mir genauer überlege.

Ach ja:
user profile iconHeiko hat folgendes geschrieben Zum zitierten Posting springen:
repne liefert aber die Position 9 zurück - also schon hinter dem String.
Naja, nicht ganz, die (unsichtbaren) jeweils 2 Zeilenumbruchszeichen darfst du auch nicht vergessen, dahinter wäre das nicht ;-).
Heiko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Mo 13.10.08 14:35 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Ganz einfach: ecx wird ja heruntergezählt bei der Schleife, und sobald das 0 ist (Stringende) oder das Zeroflag gesetzt ist wird abgebrochen bei repne.

Mhm, kann man den heraufzählen lassen? Ich möchte ja den nächsten Zeilenumbruch finden und nicht irgendeinen (sry in ASM bin ich nicht ganz standfest ;) ).

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

Ach ja:
user profile iconHeiko hat folgendes geschrieben Zum zitierten Posting springen:
repne liefert aber die Position 9 zurück - also schon hinter dem String.
Naja, nicht ganz, die (unsichtbaren) jeweils 2 Zeilenumbruchszeichen darfst du auch nicht vergessen, dahinter wäre das nicht ;-).

Mist hast Recht. Hab vergessen die zwei Zeichen im Zeilenumbruch mitzuzählen ;).
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 13.10.08 14:47 
user profile iconHeiko hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Ganz einfach: ecx wird ja heruntergezählt bei der Schleife, und sobald das 0 ist (Stringende) oder das Zeroflag gesetzt ist wird abgebrochen bei repne.

Mhm, kann man den heraufzählen lassen? Ich möchte ja den nächsten Zeilenumbruch finden und nicht irgendeinen (sry in ASM bin ich nicht ganz standfest ;) ).
Halt! Nicht falsch verstehen ;-). Es wird nicht von hinten gesucht, nur die Zählung im Register zählt rückwärts, da eine Prüfung auf 0 einfacher ist als ein Vergleich mit der Stringlänge.
In dem Register steht zuerst die Länge des Strings, die hast du ja hineinkopiert, und dann werden die Zeichen der Reihe nach verglichen und dabei heruntergezählt. Beim ersten Zeilenumbruch von vorne an wird abgebrochen.
ausblenden Quelltext
1:
2:
13   12   11    10     9
   a    b    c     #13
Heiko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Mo 13.10.08 14:52 
Ahhh alles klar. Danke. Da kann ich das ja endlich weitermachen :).
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: Mo 13.10.08 18:54 
Poste mal bitte wenn du Ergebnisse hast...

Hab damit gerade mal ein bißchen rumgespielt, erste Feststellung war dass die Laufzeit um ca. 60% ansteigt wenn ich vorher den Cache mit Müll überschreibe, eine while- statt for-Schleife brachte 15%, ein Entrollen der Schleife und Doppelwortzugriff fast eine Verdoppelung :D
Heiko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Di 14.10.08 16:08 
Testergebnisse poste ich, sobald ich welche habe ;).

Allerdings habe ich gerade ein kleines Prob. Ich hab mir mal die Testversion von 2009 installiert und die meckrt den ASM-Code an.

Und zwar das mov al, Chr, wobei Chr ein Char ist. Fehlermeldung
ausblenden Quelltext
1:
[DCC Fehler] UniCodeFileStream.pas(232): E2107 Operandengröße stimmt nicht überein					


€: Ok das Char ist jetzt 16bit groß. Weiß einer zufälligerweise ob es für das repne auch eine 16bit-Version gibt bzw. wie man char als 8Bit definiert?
€2: Ah ok. Es heißt nicht CharA, sondern AnsiChar :).
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Di 14.10.08 21:33 
Hallo,

warum nutzt denn niemand
fastcode.sourceforge.net/
fastcode.sourceforge...content/CharPos.html

repne ist doch nur für die Anzahl:

fredriks.de/8086Assembler/Asm_S.htm#SCAS

ausblenden Delphi-Quelltext
1:
2:
scasb{yte}
scasw{ord}

Stringverarbeitung

www.fh-wedel.de/~wol/ass/ ab Vorlesung am 20.06.2005 (und am 30.05.2005) in HS4

Gruß Horst
Heiko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Sa 18.10.08 10:32 
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
warum nutzt denn niemand
fastcode.sourceforge.net/
fastcode.sourceforge...content/CharPos.html

Der Code ist inkompatibel zur aktuellen Delphiversion (Char = WideChar und nicht mehr AnsiChar)... Und ich mag die MPL nicht (wenn ichs richtig verstehe muss ein Teil des Codes ja OpenSource sein, was ich aber nicht voraussetzten möchte) ;).

Nichts desto trotz ein interessantes Projekt.


user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
repne ist doch nur für die Anzahl:

fredriks.de/8086Assembler/Asm_S.htm#SCAS

ausblenden Delphi-Quelltext
1:
2:
scasb{yte}
scasw{ord}

Stringverarbeitung

www.fh-wedel.de/~wol/ass/ ab Vorlesung am 20.06.2005 (und am 30.05.2005) in HS4

Hast du ein kommentiertes Beispiel dafür?

Btw: Wenn ich das hier richtig interpretiere braucht man bei diesen Befehlen ja weiterhin repne.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 18.10.08 13:25 
user profile iconHeiko hat folgendes geschrieben Zum zitierten Posting springen:
Und ich mag die MPL nicht (wenn ichs richtig verstehe muss ein Teil des Codes ja OpenSource sein, was ich aber nicht voraussetzten möchte) ;).
Die MPL erlaubt die darunter lizenzierten Code auch in proprietären Code einzubinden, solange du alle verwendete Units mit MPL-lizenziertem Code auch unter der MPL zur Verfügung stellst. Und das einige Zeit oder so.
Heißt: Solange du nur MPL-Units einbindest musst du auch nur diese weiter unter der MPL zur Verfügung stellen, wenn du dein Projekt veröffentlichst, dein eigener Code muss nicht Open Source sein, wenn er in anderen Units liegt.

Wie da genau alle Bedingungen sind weiß ich nicht so genau, aber so habe ich das in Erinnerung.

user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
repne ist doch nur für die Anzahl:
Wie meinst du das? repne führt doch die Schleife bis zur Abbruchbedingung aus, nämlich bis zum ersten Fund in diesem Fall. :nixweiss:
Heiko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: So 19.10.08 00:02 
Zur Urspungsversion: kann man hier noch etwas optimieren?

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
function CharPos(const PStr: PAnsiChar; Chr: AnsiChar; StrLen: Integer): Integer;
asm
  push edi
  push ebx
  mov edi, eax           // In PStr suchen
  mov ebx, ecx           // Max. Stringlänge suchen (BackUp)
  mov al, dl             // Chr suchen
  cld                    // Vorwärts
  repne scasb            // Byteweise nach al suchen
  JNC @@FOUND            // was gefunden->springen
  xor eax,eax            // 0 als Ergebnis zurückgeben, wenn nix gefunden
  JMP @@END

  @@FOUND:
  mov eax, ebx           //Ergebnis in Result speichern
  sub eax, ecx           //Differenz bilden
  @@END:
  pop ebx
  pop edi
end;
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 20.10.08 17:15 
Hallo,

Laut Fastcode ist CharPos_Sha_Pas_2_c der schnellste Code, der nicht MMX/SSE 1,2... nutzt (Die sind 50 % schneller) . Minimal schneller noch als Assembler-Variante dieses Code's .

Grauselig mit goto..

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
function CharPos_Sha_Pas_2_c(ch: char; const s: AnsiString): integer;
const
  cMinusOnes = -$01010101;
  cSignums   =  $80808080;
var
  Ndx, Len, c, d, Mask, Sign, Save, SaveEnd: integer;
label
  Small, Middle, Large,
  Found0, Found1, Found2, Found3, NotFound,
  Matched, MatchedPlus1, MatchedMinus1, NotMatched,
  Return;
begin
  c:=integer(@pchar(integer(s))[-4]);
  if c=-4 then goto NotFound;
  Len:=pinteger(c)^;
  if Len>24 then goto Large;
  Ndx:=4;
  if Ndx>Len then goto Small;

Middle:
  if pchar(c)[Ndx+0]=ch then goto Found0;
  if pchar(c)[Ndx+1]=ch then goto Found1;
  if pchar(c)[Ndx+2]=ch then goto Found2;
  if pchar(c)[Ndx+3]=ch then goto Found3;
  inc(Ndx,4);
  if Ndx<=Len then goto Middle;

  Ndx:=Len+1;
  if pchar(c)[Len+1]=ch then goto Found0;
  if pchar(c)[Len+2]=ch then goto Found1;
  if pchar(c)[Len+3]<>ch then goto NotFound;
  Result:=integer(@pchar(Ndx)[-1]); exit;
  goto Return; //drop Ndx

Small:
  if Len=0 then goto NotFound; if pchar(c)[Ndx+0]=ch then goto Found0;
  if Len=1 then goto NotFound; if pchar(c)[Ndx+1]=ch then goto Found1;
  if Len=2 then goto NotFound; if pchar(c)[Ndx+2]<>ch then goto NotFound;

Found2: Result:=integer(@pchar(Ndx)[-1]); exit;
Found1: Result:=integer(@pchar(Ndx)[-2]); exit;
Found0: Result:=integer(@pchar(Ndx)[-3]); exit;
NotFound: Result:=0; exit;
  goto NotFound; //kill warning 'Ndx might not have been initialized'
Found3: Result:=integer(@pchar(Ndx)[0]); exit;
  goto Return; //drop Ndx

Large:
  Save:=c;
    Mask:=ord(ch);
  Ndx:=integer(@pchar(c)[+4]);

    d:=Mask;
  inc(Len,c);
  SaveEnd:=Len;
    Mask:=(Mask shl 8);
  inc(Len,+4-16+3);

    Mask:=Mask or d;
  Len:=Len and (-4);
    d:=Mask;
  cardinal(Sign):=cSignums;

    Mask:=Mask shl 16;
  c:=pintegerArray(Ndx)[0];
    Mask:=Mask or d;
  inc(Ndx,4);

    c:=c xor Mask;
    d:=integer(@pchar(c)[cMinusOnes]);
    c:=c xor (-1);
    c:=c and d;
    d:=Mask;

    if c and Sign<>0 then goto MatchedMinus1;
    Ndx:=Ndx and (-4);
    d:=d xor pintegerArray(Ndx)[0];

    if cardinal(Ndx)<cardinal(Len) then repeat;
      c:=integer(@pchar(d)[cMinusOnes]);
      d:=d xor (-1);
      c:=c and d;
      d:=Mask;

      d:=d xor pintegerArray(Ndx)[1];
      if c and Sign<>0 then goto Matched;
      c:=integer(@pchar(d)[cMinusOnes]);
      d:=d xor (-1);
      c:=c and d;
      d:=pintegerArray(Ndx)[2];
      if c and Sign<>0 then goto MatchedPlus1;
      d:=d xor Mask;

      c:=integer(@pchar(d)[cMinusOnes]);
      d:=d xor (-1);
      inc(Ndx,12);
      c:=c and d;

      //if c and Sign<>0 then goto MatchedMinus1;
      d:=Mask;
      if c and Sign<>0 then goto MatchedMinus1;
      d:=d xor pintegerArray(Ndx)[0];
      until cardinal(Ndx)>=cardinal(Len);

    Len:=SaveEnd;
    while true do begin;
      c:=integer(@pchar(d)[cMinusOnes]);
      d:=d xor (-1);
      c:=c and d;
      inc(Ndx,4);
      if c and Sign<>0 then goto MatchedMinus1;
      d:=Mask;
      if cardinal(Ndx)<=cardinal(Len)
      then d:=d xor pintegerArray(Ndx)[0]
      else begin;
        if Len=0 then goto NotMatched;
        d:=d xor pintegerArray(Len)[0];
        Ndx:=Len;
        Len:=0;
        end
      end;

NotMatched:
  Result:=0; exit;

MatchedPlus1:   inc(Ndx,8);
MatchedMinus1:  dec(Ndx,4);
Matched:
    c:=c and Sign;
    dec(Ndx,integer(Save)+2);
    if word(c)=0 then begin;
      c:=c shr 16; inc(Ndx,2);
      end;
    if byte(c)<>0 then dec(Ndx);
    Result:=Ndx;
Return:
  end;

Wie Du erkennst, werden bei langen Strings(len>24) 4 Byte aufeinmal verarbeitet.
Das macht es schnell.Rate mal wie viele MMX und SSE vergleichen.
Die Geschwindigkeit hängt jetzt sehr davon, ob Du sehr lange oder kurze Strings untersuchst.

www.math.uni-wuppert...R-SS02/opcode_i.html
Dort steht ja 4 Takte für einen Pentium pro Vergleich mittels scas(..b/w/d)
Also 16 Takte pro integer.

Teste es doch mal selbst in einem Vergleich.
erzeuge einen String mit #1 bis #255 und lasse darin nach #1.. #255 suchen len ist eben immer 255
Oder du testest mit verschiedenen Längen 1,4,5,23,24,32,33,63,64,255.

Viel Spass dabei.

Gruß Horst