Intel x86 Assembly Language & Microarchitecture
Optymalizacja
Szukaj…
Wprowadzenie
Uwagi
W razie wątpliwości zawsze możesz odwołać się do dość obszernego Podręcznika na temat optymalizacji architektury Intel 64 i IA-32 , który jest świetnym zasobem od firmy stojącej za architekturą x86.
Zerowanie rejestru
Oczywistym sposobem zerowania rejestru jest MOV w 0 - na przykład:
B8 00 00 00 00 MOV eax, 0
Zauważ, że jest to instrukcja 5-bajtowa.
Jeśli chcesz zablokować flagi ( MOV nigdy nie wpływa na flagi), możesz użyć instrukcji XOR do bitowego-XOR-u samego rejestru:
33 C0 XOR eax, eax
Ta instrukcja wymaga tylko 2 bajtów i jest wykonywana szybciej na wszystkich procesorach .
Przenoszenie flagi Carry do rejestru
tło
Jeśli flaga Carry ( C ) zawiera wartość, którą chcesz umieścić w rejestrze, naiwnym sposobem jest zrobienie czegoś takiego:
mov al, 1
jc NotZero
mov al, 0
NotZero:
Użyj „sbb”
Bardziej bezpośrednim sposobem uniknięcia skoku jest użycie opcji „Odejmij z pożyczeniem”:
sbb al,al ; Move Carry to al
Jeśli C wynosi zero, to al będzie wynosić zero. W przeciwnym razie będzie to 0xFF ( -1 ). Jeśli potrzebujesz 0x01 , dodaj:
and al, 0x01 ; Mask down to 1 or 0
Plusy
- O tym samym rozmiarze
- Dwie lub jedna instrukcja mniej
- Bez drogiego skoku
Cons
- Jest nieprzejrzysty dla czytelnika niezaznajomionego z techniką
- Zmienia inne flagi
Przetestuj rejestr pod kątem 0
tło
Aby dowiedzieć się, czy rejestr zawiera zero, naiwną techniką jest zrobienie tego:
cmp eax, 0
Ale jeśli spojrzysz na kod operacji, otrzymujesz:
83 F8 00 cmp eax, 0
Użyj test
test eax, eax ; Equal to zero?
Sprawdź otrzymany kod operacyjny:
85 c0 test eax, eax
Plusy
- Tylko dwa bajty!
Cons
- Nieprzezroczysty dla czytelnika niezaznajomionego z techniką
Możesz także zajrzeć do pytania i odpowiedzi na temat tej techniki .
Wywołania systemowe w systemie Linux są mniej rozdęte
W 32-bitowym systemie Linux wywołania systemowe są zwykle wykonywane przy użyciu instrukcji sysenter (mówię zwykle, ponieważ starsze programy używają teraz przestarzałej int 0x80 ), jednak może to zająć sporo miejsca w programie, a więc są sposoby, że jeden może skracać rogi, aby skracać i przyspieszać.
Zwykle jest to układ wywołania systemowego w 32-bitowym systemie Linux:
mov eax, <System call number>
mov ebx, <Argument 1> ;If applicable
mov ecx, <Argument 2> ;If applicable
mov edx, <Argument 3> ;If applicable
push <label to jump to after the syscall>
push ecx
push edx
push ebp
mov ebp, esp
sysenter
To ogromne prawo! Ale jest kilka sztuczek, które możemy wyciągnąć, aby uniknąć tego bałaganu.
Pierwszym z nich jest ustawienie ebp na wartość esp zmniejszoną o rozmiar 3 32-bitowych rejestrów, czyli 12 bajtów. Jest to świetne, o ile jesteś w stanie nadpisywać ebp, edx i ecx śmieciami (np. Kiedy i tak przeniesiesz wartość do tych rejestrów bezpośrednio po tym), możemy to zrobić za pomocą instrukcji LEA, abyśmy nie potrzebowali wpływać na wartość samego ESP.
mov eax, <System call number>
mov ebx, <Argument 1>
mov ecx, <Argument 2>
mov edx, <Argument 3>
push <label to jump to after the syscall>
lea ebp, [esp-12]
sysenter
Jednak nie skończyliśmy, jeśli wywołanie systemowe to sys_exit, możemy uciec od nie wypychania niczego na stos!
mov eax, 1
xor ebx, ebx ;Set the exit status to 0
mov ebp, esp
sysenter
Pomnóż przez 3 lub 5
tło
Aby uzyskać produkt rejestru i stałej i przechowywać go w innym rejestrze, naiwnym sposobem jest zrobienie tego:
imul ecx, 3 ; Set ecx to 5 times its previous value
imul edx, eax, 5 ; Store 5 times the contend of eax in edx
Użyj lea
Mnożenie to kosztowne operacje. Szybciej jest używać kombinacji przesunięć i dodatków. W szczególnym przypadku zmultiplikowania rywalizacji rejestru 32- lub 64-bitowego, który nie jest esp lub rsp przez 3 lub 5, możesz użyć instrukcji lea. Wykorzystuje obwód obliczania adresu do szybkiego obliczania produktu.
lea ecx, [2*ecx+ecx] ; Load 2*ecx+ecx = 3*ecx into ecx
lea edx, [4*edx+edx] ; Load 4*edx+edx = 5*edx into edx
Wielu asemblerów również zrozumie
lea ecx, [3*ecx]
lea edx, [5*edx]
Dla wszystkich możliwych mnożników innych ebp lub rbp instrukcji jest taka sama jak przy użyciu imul .
Plusy
- Wykonuje się znacznie szybciej
Cons
- Jeśli twój multiplicand to
ebplubrbp, zajmuje im jeden bajt więcej za pomocąimul - Więcej do wpisania, jeśli asembler nie obsługuje skrótów
- Nieprzezroczysty dla czytelnika niezaznajomionego z techniką