Autor |
Beitrag |
Heiko
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: 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?
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 mov ecx, StrLen mov al, Chr cld repne scasb 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;
begin SetLength(Buffer, bufMaxSize); start := position;
chrPos := 1; bufSize := Read(Buffer[chrPos], bufMaxSize); while(bufSize<>0) do 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
|
Verfasst: 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.
      
Beiträge: 1876
Erhaltene Danke: 129
Win 8.1, Xubuntu 15.10
|
Verfasst: So 12.10.08 20:12
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.
€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
      
Beiträge: 478
Windows XP Home
Delphi 2005, RAD Studio 2007, MASM32, FASM, SharpDevelop 3.0
|
Verfasst: 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 BenBE 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
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 13.10.08 00:13
Nana ...nicht mir meine Erklärungen verdrehen
Silas hat folgendes geschrieben : | EBX muss immer gesichert werden (natürlich nur wenns geändert wird), EDI und ESI nur bei Klassenfunktionen (hat mir BenBE 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
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: 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
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 13.10.08 01:22
_________________ 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
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: Mo 13.10.08 01:55
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 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: 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?
Marc. hat folgendes geschrieben : | €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?
Thorsten83 hat folgendes geschrieben : | 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
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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: Heiko hat folgendes geschrieben : | 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 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Mo 13.10.08 14:35
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 13.10.08 14:47
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Mo 13.10.08 14:52
Ahhh alles klar. Danke. Da kann ich das ja endlich weitermachen  .
|
|
Thorsten83
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: 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 
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: 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
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
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: 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
Delphi-Quelltext
Stringverarbeitung
www.fh-wedel.de/~wol/ass/ ab Vorlesung am 20.06.2005 (und am 30.05.2005) in HS4
Gruß Horst
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Sa 18.10.08 10:32
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.
Hast du ein kommentiertes Beispiel dafür?
Btw: Wenn ich das hier richtig interpretiere braucht man bei diesen Befehlen ja weiterhin repne.
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 18.10.08 13:25
Heiko hat folgendes geschrieben : | 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.
Horst_H hat folgendes geschrieben : | 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. 
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: So 19.10.08 00:02
Zur Urspungsversion: kann man hier noch etwas optimieren?
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 mov ebx, ecx mov al, dl cld repne scasb JNC @@FOUND xor eax,eax JMP @@END
@@FOUND: mov eax, ebx sub eax, ecx @@END: pop ebx pop edi end; |
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: 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..
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; 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; Found3: Result:=integer(@pchar(Ndx)[0]); exit; goto Return; 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;
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
|
|
|