Assembler Tricks

Hallo Gemeinde!


sorry falls sowas schonmal diskutiert wurde, aber ich wusste nicht genau was ich in die Suchmaske eingeben könnte, um dementsprechende Beiträge zu finden.


Nun zu meiner Frage:

Mich würde speziell interessieren, warum man bei der Assemblerprogrammierung den folgenden Trick anwendet:

xor ax, ax um das ax Register zu löschen.

Könnte man nicht einfach schreiben:

mov ax, 0


Beschäftige mich erst seit einer Woche mit der Assembler-programmierung, um die Zusammenhänge der Hochsprache C++ besser zu verstehen. Es wäre schön, wenn mir jemand diesen Trick genauer erklären könnte.

Grüße Sp4nkie.
 
Soweit ich weiss spart man sich da auch 1-2 Byte ein. Und wenn man nur geringen Speicher zur Verfügung hat, ist sowas halt eine Menge.
 
Original von Heinzelotto
geht auf manchen prozessoren schneller (glaube ich)

dieses Argument hab ich auch noch so im Hinterkopf...

XOR braucht definitiv nur 1 Takt...
wobei ich der Meinung bin, dass MOV auch nur einen Takt brauchen sollte... (rein von dem, was da im Prozessor vor sich geht...)

hab jetzt eine Weile gegoogelt und noch immer keine Tabelle gefunden, wo hinter den Instruktionen auch mal die Taktzahl steht (irgendwo hier im Raum muss noch meine gute alte x86-Assembler-Instr.-Liste sein, wo das drauf stand... finde mich aber gerade nicht durch mein Chaos)


aber einen Vorteil von XOR gegenüber MOV hab ich noch gefunden

XOR braucht weniger Speicherplatz
http://www.softgames.de/forum/frage120240.html
xor eax,eax = 31h C0h = 2 Bytes

xor rax,rax = 48h 31h C0h = 3 Bytes

mov eax,0 = B8h 00h 00h 00h 00h = 5 Bytes

mov rax,0 = 48h C7h C0h 00h 00h 00h 00h = 7 Bytes

edit: oops, da war wohl einer schneller...
 
Früher war dies tatsächlich schneller (wir reden über 386/486). Dann haben sich die CPU Hersteller an die üblichen Codegenerierungsmuster angepasst, so dass doch recht viele Tricks nicht mehr wirklich schneller sind (z.B das "INC Register" ist bei mir langsamer, als ADD Register,1). Bei den "gewöhnlichen" CPUs werkeln ja im inneren auch noch Microprogramme (Stichwort Microcode ) - die dann entsprechend angepasst werden ;) . Bei Interesse gibt es hier etwas aktuelleres:
http://agner.org/optimize/

Edit: jep, kann mich auch ganz genau an eine Tabelle mit 286,386 und 586 + clock Angaben erinnern - aber diese lässt sich nicht mehr auffinden :)
 
Hallo,
dieses sieht man oft wenn man Shellcodes schreiben will. Dort sind 0-Bytes (\x00) nicht erlaubt, da das Zielprogramm aufhören würde weiterzuarbeiten.
Möchte man dann aber in seinem Shellcode ein Register auf 0 setzen, funktioniert mov ax, 0 nicht, da dort \x00 auftaucht, weswegen man dann z.B. auf xor ax, ax zurückgreifen muss.
 
Einerseits steht es irgendwo in der Doku, anderseits:
Code:
; ??????????????????????????????????????????????????????????????
    .586                       ; create 32 bit code
    .model flat, stdcall       ; 32 bit memory model
    option casemap :none       ; case sensitive
 
    include \masm32\include\windows.inc
    include \masm32\include\masm32.inc
    include \masm32\include\kernel32.inc

    includelib \masm32\lib\masm32.lib
    includelib \masm32\lib\kernel32.lib
    include timers.asm

    include \masm32\macros\macros.asm

    include timers.asm
; ??????????????????????????????????????????????????????????????
    .code
; ??????????????????????????????????????????????????????????????
start: 
; ??????????????????????????????????????????????????????????????
    align 4
        timer_begin 1000, REALTIME_PRIORITY_CLASS ;die schleife wird insgesamt 1000 000 000 durchlaufen
        mov ebx,0 
        LOOP1:
          mov ecx,0
          LOOP2:
            add ecx,1
            cmp ecx,1000
            jbe LOOP2
          add ebx,1
          cmp ebx,1000
          jbe LOOP1  
 

    timer_end
    print str$(eax)
    print chr$(" ms, add 1 Loop'",13,10)
    
    align 4
        timer_begin 1000, REALTIME_PRIORITY_CLASS ;die schleife wird insgesamt 1000 000 000 durchlaufen
        mov ebx,0
        LOOP11:
          mov ecx,0
          LOOP22:
            inc ecx
            cmp ecx,1000
            jbe LOOP22
          add ebx,1
          cmp ebx,1000
          jbe LOOP11  
 

    timer_end
    print str$(eax)
    print chr$(" ms, 'inc Loop'",13,10)

   
 

mov   eax, input(13,10,"Press enter to exit...")
exit

; ??????????????????????????????????????????????????????????????
end start
Ist nur als Beispiel wie man es andwenden kann - die TIMERS.INC hänge ich mal an - sonst lässt es sich mit MASM und
Code:
\masm32\bin\ml /c /coff /nologo test.asm
\masm32\bin\Link /SUBSYSTEM:CONSOLE /MERGE:.rdata=.text test.obj > nul
assemblieren.
Wobei ich hier auf einem Coure2Duo wieder andere Werte herausbekomme, als auf dem P4 (beide sind gleichschnell bzw der Unterschied ist +-2ms).

Zugegebenermaßen habe ich es irgendwann mal aufgegeben, auf großartige Handoptimierungen zu achten und die aktuelle Entwicklung mitzuverfolgen - weil es einfach viel zu Aufwändig wird und im Verhältniss gar nicht so viel bring.
Allerdings (imho) lohnt es sich trotzdem noch, etwas in diese Richtung zu machen, weil man dann auch in höheren Sprachen praktisch automatisch andere Entwurfsmuster anwendet oder zumindest bestimmte Details anders implementiert:
Beispielsweise sieht man dann den großen Unterschied hier:
Code:
myarray[][]=new int[1000][1000];
for (i=0;i<1000;i++)
  for (j=0;j<1000;j++)
    myarray[j][i]=1;

versus:
for (i=0;i<1000;i++)
  for (j=0;j<1000;j++)
    myarray[i][j]=1;
aber das ist schon zu weit OT ;)
 
Zählschleifen kann man auch so optimieren... Aber wer macht sowas schon?
Code:
for (j=999;j--;)
     do_sometihing();
 
Original von mauralix
Code:
for (j=999;j--;)
     do_sometihing();

wenn, dann "--j" statt "j--".
Der Prä-Inkrement- (und auch der Prä-dekrement-)operator ist nämlich immer schneller:

Code:
Type operator++(int) { // implementierung des postinkrement
   Type tmp = *this;
   increment_this();
   return tmp;
}

Type& operator++() { // implementierung des präinkrement
   increment_this();
   return *this;
}

beim postinkrement muss erst ein temporäres objekt des Typs erzeugt werden, was je nach Typgröße einige Zeit in Anspruch nehmen kann. Wo also nicht unbedingt das Postinkrement nötig ist, sollte man das Präinkrement benutzen.
Inwieweit der Compiler das optimiert, ist mir allerdings nicht bekannt, ich bin mir ziemlich sicher, dass er es bei eingebauten datentypen wie "int" wegoptimiert, aber bei iteratoren über stl-container oder eigene typen sieht es bestimmt schon anders aus.
 
danke erstmal für eure Antworten. Hatte auch schon davon gelesen, dass man Assembler dazu benutzt um Code zu optimieren.

Grüße Sp4nkie
 
Also in der DemoScene sind Assemblerkentnisse noch wichtig, wenn man möglichst viel in ein 4k demo packen will. :D

PS: Danke CDW für deine guten beiträge.
 
Zurück
Oben