Wie wird das Registerflagbei CMPSD gesetzt?


  • Mod

    FrEEzE2046 schrieb:

    Muss der Prozessor nicht immer die Schritte von "2" durchführen? Im Endeffekt ist "1" doch nur eine kürzere Schreibweise oder bringt dass tatsächlich auch sonst noch was?

    Mit lea ist es wahrscheinlich schneller, zudem werden keine Flags beeinflusst.



  • camper schrieb:

    FrEEzE2046 schrieb:

    Muss der Prozessor nicht immer die Schritte von "2" durchführen? Im Endeffekt ist "1" doch nur eine kürzere Schreibweise oder bringt dass tatsächlich auch sonst noch was?

    Mit lea ist es wahrscheinlich schneller, zudem werden keine Flags beeinflusst.

    Ich hab mal meinen Beitrag editiert. Weißt du woran es liegen kann?


  • Mod

    FrEEzE2046 schrieb:

    camper schrieb:

    FrEEzE2046 schrieb:

    Muss der Prozessor nicht immer die Schritte von "2" durchführen? Im Endeffekt ist "1" doch nur eine kürzere Schreibweise oder bringt dass tatsächlich auch sonst noch was?

    Mit lea ist es wahrscheinlich schneller, zudem werden keine Flags beeinflusst.

    Ich hab mal meinen Beitrag editiert. Weißt du woran es liegen kann?

    Weil due esi/edi andersherum initialisierst, nebenbei fehlt bei dir noch die Adresskorrektur vor @@Rest.



  • camper schrieb:

    Ich hab mal meinen Beitrag editiert. Weißt du woran es liegen kann?

    Weil due esi/edi andersherum initialisierst, nebenbei fehlt bei dir noch die Adresskorrektur vor @@Rest.[/quote]

    Du hast recht danke.

    Zur Adresskorrektur:

    add		edi, ecx   	// byte offset
        add		esi, ecx
    

    mache ich doch.

    EDIT:

    Also, sieht jetzt so aus:

    push	edi
        push	esi
    
        std											// set direction flag to decrement
        lea		esi, [edx+ecx]
        lea		edi, [eax+ecx]
    
        xor		eax, eax 					// null result
        mov		edx, ecx					// save value
    
        shr		ecx, 2						// look for dword align
        jz		@@Rest
    
        lea		esi, [esi-4]			// position on last DWORD
        lea		edi, [edi-4]
    
        repe	cmpsd
        jne		@@Exit
    
      @@Rest:
        mov		ecx, edx
        and		ecx, 3						// remaining bytes
        jz		@@Exit
    
        lea		esi, [esi+ecx] 		// byte offset
        lea		edi, [edi+ecx]
    
        repe	cmpsb
    
      @@Exit:
      	cld
        seta  al
        sbb   eax, 0
    
        pop		esi
        pop		edi
    

    Noch 2 Sachen dazu:

    1. Lohnen sich die Vergleiche inkl. jump oder sollte ich lieber (so wie du) einfach cmpsb/d drüber laufen lassen, auch wenn in ecx 0 steht?

    2. Bei mir sind die Ergebniswerte immer noch vertauscht. Verstehe ich nicht wirklich.


  • Mod

    FrEEzE2046 schrieb:

    Zur Adresskorrektur:

    add		edi, ecx   	// byte offset
        add		esi, ecx
    

    mache ich doch.

    Woher weisst du, dass in ecx genau siezof(dword)-sizeof(byte)=3 drin steht? Abgesehen davon passt das mit dem Fall size<=3 ohnehin nicht.



  • camper schrieb:

    FrEEzE2046 schrieb:

    Zur Adresskorrektur:

    add		edi, ecx   	// byte offset
        add		esi, ecx
    

    mache ich doch.

    Woher weisst du, dass in ecx genau siezof(dword)-sizeof(byte)=3 drin steht?

    Ich weiß, dass in ecx die restlichen Bits sind, welche nicht dword aligned sind und die addiere ich drauf. Ist doch korrekt würde ich sagen.


  • Mod

    FrEEzE2046 schrieb:

    Ist doch korrekt würde ich sagen.

    Dann schau dir nochmal mein Beispiel auf der ersten Seite an. ecx hätte dort den Wert 2, die Korrektor würde dazu führen, dass esi, edi zum Anfang der Strings zeigen.
    Da noch zwei Zeichen zu vergleichen sind, vergleichst du das ersten Zeichen und das Zeichen davor (welches schon nicht mehr dazugehört), während das zweite Stringelement übersprungen wird.



  • camper schrieb:

    FrEEzE2046 schrieb:

    Ist doch korrekt würde ich sagen.

    Dann schau dir nochmal mein Beispiel auf der ersten Seite an.

    Hab ich wirklich zu genüge. Kannst du mir bitte den Fehler zeigen? Ich bin anscheinend blind dafür.

    EDIT: Wovon sprichst du jetzt eigentlich? Den Byte Offset wiederherstellen oder esi/edi initialisieren


  • Mod

    siehe Edit oben



  • camper schrieb:

    siehe Edit oben

    Irgendwie stimmt jetzt irgendwas überhaupt nicht. Vorher hat es funktioniert, jetzt habe ich es "kaputt optimiert" 😉

    var
      s1, s2 : String[6];
    begin
    	s1 := 'aaaaaa';
      s2 := 'aaaaaa';
    
      writeln(MemCmp(s1[1], s2[2], 6));
    

    1. Ich stehe an Position s1[1] = Speicheradresse: 4458497
    2. Ich führe aus

    lea esi, [eax+ecx]
    

    und stehe auf: 4458503
    3. Da ich mich auf dem letzten DWORD positionieren möchte, führe ich aus

    lea esi, [esi-4]
    

    und stehe auf: 4458499
    4. Jetzt führe ich aus:

    repe cmpsb
    

    und

    jne @@Exit
    

    führt mich zur Exit-Marke, obwohl der Vergleich eigentlich "equals" hätte ergeben müssen.

    Was läuft da schief?


  • Mod

    Wieso ziehst du 4 ab, wenn du byteweise vergleichst?
    Wie sieht der COde denn aktuell aus?



  • camper schrieb:

    Wieso ziehst du 4 ab, wenn du byteweise vergleichst?
    Wie sieht der COde denn aktuell aus?

    Entschuldige, war ziemlicher quatsch. So ist er jetzt "final"

    push	edi
        push	esi
    
        std											// set direction flag to decrement
        lea		esi, [eax+ecx-1]
        lea		edi, [edx+ecx-1]
    
        xor		eax, eax 					// null result
        mov		edx, ecx					// save value
    
        shr		ecx, 2						// look for dword align
        jz		@@Rest
    
        lea		esi, [esi-3]			// position on last DWORD
        lea		edi, [edi-3]
    
        repe	cmpsd
        jne		@@Exit
    
      @@Rest:
        mov		ecx, edx
        and		ecx, 3						// remaining bits
        jz		@@Exit
    
        lea		esi, [esi+ecx+2]  // byte offset
        lea		edi, [edi+ecx+2]
    
        repe	cmpsb
    
      @@Exit:
      	cld
        seta  al
        sbb   eax, 0
    
        pop		esi
        pop		edi
    

    Wobei ich denke, dass man noch optimieren kann. Insbesondere die jumps, sowie die lea Befehle scheinen mir noch nicht perfekt.

    Vielleicht irre ich mich, aber ich kann mir nicht vorstellen, dass ein

    lea esi, [esi+ecx+2]
    

    sonderlich schnell ist. Der Prozessor hat doch keine andere Wahl als

    add edi, ecx
    add edi, 2
    

    intern zu machen oder?

    Kann man den VC 9.0 Compiler eigentlich irgendwie seine Macken austreiben. Aus folgendem Code

    __asm {
    	push	esi
    	lea	esi, [edx+ecx-1]
    }
    

    macht er nämlich immer:

    ; Line 50
    	push	esi
    ; Line 52
    	push	esi
    ; Line 54
    	lea	esi, DWORD PTR [edx+ecx-1]
    ; Line 56
    	pop	esi
    ; Line 58
    	pop	esi
    	ret	0
    

  • Mod

    FrEEzE2046 schrieb:

    Vielleicht irre ich mich, aber ich kann mir nicht vorstellen, dass ein

    lea esi, [esi+ecx+2]
    

    sonderlich schnell ist. Der Prozessor hat doch keine andere Wahl als

    add edi, ecx
    add edi, 2
    

    intern zu machen oder?

    Sinngemäß sicher, allerdings gibt es eigene Recheneinheiten für Adressberechnungen, die entsprechend schnell ist, schließlich treten komplexe Addressierungsarten öfter auf. Etwas anderes wäre es evtl., wenn du das Ganze auf einem 386er ausführen würdest... Nichts hindert dich, das Ganze einfach mal zu messen, statt nur zu raten.
    Wo kommen jetzt eigentlich plötzlich die +ecx+2 her?

    FrEEzE2046 schrieb:

    Kann man den VC 9.0 Compiler eigentlich irgendwie seine Macken austreiben. Aus folgendem Code

    __asm {
    	push	esi
    	lea	esi, [edx+ecx-1]
    }
    

    macht er nämlich immer:

    ; Line 50
    	push	esi
    ; Line 52
    	push	esi
    ; Line 54
    	lea	esi, DWORD PTR [edx+ecx-1]
    ; Line 56
    	pop	esi
    ; Line 58
    	pop	esi
    	ret	0
    

    Das ist keine Macke, sondern Notwendigkeit. Wenn im inline-Assemblercode Register verändert werden, die außerhalb unverändert bleiben müssen, übernimmt der Compiler für dich die nötigen push/pop Operationen. Das geht, weil bei Visual C der Compiler den Assemblercode selbst versteht. Bei g++ etwa ist das anders. Willst du den gesamten Prolog/Epilog vermeiden, muss die Funktion naked sein. Dann ist aber u.a. kein inlining mehr möglich.



  • Nunja eine naked function wäre sicherlich möglich, aber ich habe keine Lust für jede Funktion einen Pro-/Epilog zu schreiben.

    Dass was er da macht ist mir schon klar, aber wenn du schon sagst, dass er meinen Asm-Code "lesen" kann, sollte er auch erkennen, dass ich ESI-Register schon selber gesichert habe 😉

    Zum ECX+2
    Es muss natürlich ECX+3 heißen.

    Zur Performance
    Bei einem 2.000.000 fachen Vergleich sind beide gleich auf. Ich denke ohnehin, dass die ausschlaggebenden Befehle repe cmpsd / cmpsb sind. Der Rest macht nicht viel aus.


  • Mod

    FrEEzE2046 schrieb:

    Es muss natürlich ECX+3 heißen.

    Warum addierst du nochmal ecx? Das ist doch ganz am Anfang schon geschehen.



  • hier mal ne deutlich schnellere Variante (@Core2Duo):

    ; esi = lho
            ; edi = rho
            ; ecx = size
    
            test ecx,0FFFFFFFCh 
            lea edi,[edi+ecx-1]
            lea esi,[esi+ecx-1]
            jz @bytes
            lea edi,[edi-3]
            lea esi,[esi-3]
        @dwords:
            mov eax,DWORD ptr [esi]
            mov edx,DWORD ptr [edi]
            lea edi,[edi-4]
            lea esi,[esi-4]
            lea ecx,[ecx-4]
            cmp eax,edx
            mov eax,0
            seta al 
            sbb eax,0
            jnz @fin
            test ecx,0FFFFFFFCh
            jnz @dwords
            test ecx,ecx
            jz @fin
    
            lea edi,[edi+3]
            lea esi,[esi+3] 
        @bytes:
            movzx eax,BYTE ptr [esi]
            movzx edx,BYTE ptr [edi]
            lea edi,[edi-1]
            lea esi,[esi-1]
            cmp eax,edx
            mov eax,0 
            seta al 
            sbb eax,0
            jnz @fin
            dec ecx
            jnz @bytes
        @fin:
    


  • camper schrieb:

    FrEEzE2046 schrieb:

    Es muss natürlich ECX+3 heißen.

    Warum addierst du nochmal ecx? Das ist doch ganz am Anfang schon geschehen.

    Tue ich gar nicht, ich glaube wir haben uns falsch verstanden:

    { EAX -> lho          		result = 0		equal
          EDX -> rho              result = 1		lho > rho
          ECX -> size             result = -1		lho < rho	}
    
        push	edi
        push	esi
    
        std																// set direction flag to decrement
        lea		esi, DWORD PTR [eax+ecx-1]  // position on last byte of both
        lea		edi, DWORD PTR [edx+ecx-1]  // comparison operands
    
        xor		eax, eax 										// null result
        mov		edx, ecx										// save value
    
        shr		ecx, 2											// look for dword align
        jz		@@Rest
    
        lea		esi, DWORD PTR [esi-3]			// position on last DWORD of both
        lea		edi, DWORD PTR [edi-3]  		// comparison operands
    
        repe	cmpsd
        jne		@@Exit
    
      @@Rest:
        mov		ecx, edx
        and		ecx, 3											// remaining bytes
        jz		@@Exit
    
        lea		esi, DWORD PTR [esi+3]		 // align to byte equation
        lea		edi, DWORD PTR [edi+3]
    
        repe	cmpsb
    
      @@Exit:
      	cld  															// clear direction flag
        seta  al                          // set if above al to 1
        sbb   eax, 0                      // sub with borrow
    
        pop		esi
        pop		edi
    

    @cmovb
    Nicht böse gemeint: Hast du nur die Geschwindigkeit getestet oder auch die Funktionalität? Deine Funktion liefert immer 1 zurück bei mir, egal ob es wirklich größer, kleiner oder sogar gleich ist.

    Was machst du da eigentlich? Du hast doch vollkommen falsche Adressen im EDI und ESI Register, da du einfach die nimmst, die vorher schon drin standen. Das kann so ja nicht funktionieren 😉

    Außerdem vergleichst du hier:

    test ecx,0FFFFFFFCh
    

    ob in ECX 4294967292 steht - bedenke, in ECX steht ein DWORD-Typ (unsigned long). Ich denke du möchstest mit 4 vergleichen.

    Btw: Welche Rolle spielt der Core 2 Duo, wenn ich fragen darf.


  • Mod

    @cmovb: Das sieht ganz gut aus, was ergeben sich denn für Zeiten? Ein wenig loop-unrolling ist ggf. noch möglich.

    @FrEEzE2046: Die Kommentare zu cmovbs Code beachten:

    Außerdem vergleichst du hier:

    test ecx,0FFFFFFFCh
    

    ob in ECX 4294967292 steht - bedenke, in ECX steht ein DWORD-Typ (unsigned long). Ich denke du möchstest mit 4 vergleichen.

    test setzt die Flags, wie sie bei and entstehen würden. Das ist also korrekt.



  • FrEEzE2046 schrieb:

    @cmovb
    Nicht böse gemeint: Hast du nur die Geschwindigkeit getestet oder auch die Funktionalität? Deine Funktion liefert immer 1 zurück bei mir, egal ob es wirklich größer, kleiner oder sogar gleich ist.

    Du möchtest beliebig große Zahlen Vergleichen? - so hab ich das verstanden. Und ja, ich hab die Funktion getestet: sowohl auf Geschwindigkeit als auch auf Funktionalität!

    FrEEzE2046 schrieb:

    Was machst du da eigentlich? Du hast doch vollkommen falsche Adressen im EDI und ESI Register, da du einfach die nimmst, die vorher schon drin standen. Das kann so ja nicht funktionieren 😉

    nun, ich gehe mal davon aus, das du in der Lage bist die minimalen Anpassungen selber vor zu nehmen - Welcher Parameter wo drin ist hab ich ja extra angegeben!

    FrEEzE2046 schrieb:

    Außerdem vergleichst du hier:

    test ecx,0FFFFFFFCh
    

    ob in ECX 4294967292 steht - bedenke, in ECX steht ein DWORD-Typ (unsigned long). Ich denke du möchstest mit 4 vergleichen.

    test = and - überleg dir mal auf was geprüft wird

    FrEEzE2046 schrieb:

    Btw: Welche Rolle spielt der Core 2 Duo, wenn ich fragen darf.

    Die Ausführungszeiten(clocks) und Latenzen der Befehle unterscheiden sich zwischen verschieden CPU-Generation und -Herstellern.

    🕶



  • camper schrieb:

    @cmovb: Das sieht ganz gut aus, was ergeben sich denn für Zeiten? Ein wenig loop-unrolling ist ggf. noch möglich.

    @FrEEzE2046: Die Kommentare zu cmovbs Code beachten:

    Habe ich, aber sie sind doch einfach falsch. Wenn hier kein Funktionsheader angegeben ist, denke ich doch, dass er die selbe annimmt, wie ich sie gestellt hatte.
    Außerdem kenne ich keine Aufrufkonvention die direkt ins esi, edi Register schreibt.

    camper schrieb:

    test setzt die Flags, wie sie bei and entstehen würden. Das ist also korrekt.

    Das stimmt. Aber er möchte doch keine Konjunktion mit 4294967292 machen oder?


Anmelden zum Antworten