彙編代碼優化
32位代碼優化常識
原作者: Benny/29A
翻譯改寫:hume/冷雨飄心
[注意:這不是鸚鵡學舌的翻譯,我儘量以我的理解傳達原文的本意]
關於代碼優化的文章實在太多了,遺憾的是大部分我都沒有看,儘管他們就擺在我的牀邊(每當我要看的時候就忍不住打哈欠...嘿嘿).這篇文章較短所以翻了一下.
代碼優化的含義:
代碼優化的目標當然是體積小和速度快,但是在通常的情況下二者就象魚和熊掌一樣不能得兼,我們通常尋找的是這二者的折中,究竟應該偏向何方,那就得具體看我們的實際需要.
但有些常識是我們應該牢記的,下面就結合我們最常遇到的具體情況來漫談一下:
1.寄存器清0
我絕對不想再看到下面的寫法:
1) mov eax, 00000000h ;5 bytes
看起來上面的寫法很符合邏輯,但你應當意識到還有更加優化的寫法:
2) sub eax, eax ;2 bytes
3) xor eax, eax ;2 bytes
看看後面的字節數你就應該理解爲什麼要這麼作了,除此之外,在速度上也沒有損失,他們一樣快,但你喜歡xor還是sub呢?我是比較喜歡xor,原因很簡單,因爲我數學不好....
不過Microsoft比較喜歡sub....我們知道windows運行的慢....(呵呵,當然是玩笑這並不是真正原因X-D!)
2.測試寄存器是否爲0
我也不希望看到下面的代碼:
1) cmp eax, 00000000h ;5 bytes
je _label_ ;2/6 bytes (short/near)
[* 注意很多指令針對eax作了優化,你要儘可能多地實用eax,比如CMP EAX, 12345678h (5 bytes)
如果你使用其他寄存器,就是6bytes *]
讓我們看看,簡單的比較指令居然要用7/11 bytes,No No No,試試下面的寫法:
2) or eax, eax ;2 bytes
je _label_ ;2/6 (short/near)
3) test eax, eax ;2 bytes
je _label_ ;2/6 (short/near)
呵呵,只有4/8 bytes,看看我們可節省多少字節啊3/4字節...那麼接下來的問題是你喜歡OR還是TEST呢,就我個人而言,比較喜歡TEST,因爲test不改變任何寄存器,並不向任何寄存器寫入內容,這通常能在pentium機上取得更快的執行速度.
別高興的太早,因爲還有更值得我們高興的事情,假如你要判斷的的是eax寄存器,那麼看看下面的,是不是更有啓發?
4) xchg eax, ecx ;1 byte
jecxz _label_ ;2 bytes
在短跳轉的情況下我們比2)和3)又節省了1字節.oh....___...
3.測試寄存器是否爲0FFFFFFFFh
一些API返回-1,因此如何測試這個值呢?看你可能又要這樣:
1) cmp eax, 0ffffffffh ;5 bytes
je _label_ ;2/6 bytes
hey,不要這樣,寫代碼的時候想一想,於是有了下面的寫法:
2) inc eax ;1 byte
je _label_ ;2/6 bytes
dec eax ;1 byte
可以節省3 bytes並且執行速度會更快.
4.置寄存器爲0FFFFFFFFh
看看假如你是Api的作者,如何返回-1?這樣嗎?
1) mov eax, 0ffffffffh ;5 bytes
看了上面的不會再這麼XXX了吧?看看下面的:
2) xor eax, eax / sub eax, eax ;2 bytes
dec eax ;1 byte
節省一個字!還有寫法:
3) stc ;1 byte
sbb eax, eax ;2 bytes
這有時還可以優化掉1 byte:
jnc _label_
sbb eax, eax ;2 bytes only!
_label_: ...
我們爲什麼用asm呢?這就是原因.
5.寄存器清0並移入低字數值
1) xor eax, eax ;2 bytes
mov ax, word ptr [esi+xx] ;4 bytes
????--->不會吧,這可能是最多初學者的寫法了,我當然原來也是,看了benny的文章之後我決定改寫爲:
2) movzx eax, word ptr [esi+xx] ;4 bytes
收穫2 bytes!
下面的
3) xor eax, eax ;2 bytes
mov al, byte ptr [esi+xx] ;3 bytes
就相應改爲:
4) movzx eax, byte ptr [esi+xx] ;4 bytes
我們應當儘可能利用movzx
5) xor eax, eax ;2 bytes
mov ax, bx ;3 bytes
因爲執行速度不慢並通常能節省字節...
6) movzx eax, bx ;3 bytes
6.關於push,下面是着重代碼體積的優化,因爲寄存器操作總要比內存操作要快.
1) mov eax, 50h ;5 bytes
這樣就小了1 word
2) push 50h ;2 bytes
pop eax ;1 byte
當操作數只有1字節時候,push只有2 bytes,否則就是5 bytes,記住!
下一個問題,向堆棧中壓入7個0
3) push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
佔用14字節,顯然不能滿意,優化一下
4) xor eax, eax ;2 bytes
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
可以更緊湊,但會慢一點的形式如下:
5) push 7 ;2 bytes
pop ecx ;1 byte
_label_: push 0 ;2 bytes
loop _label_ ;2 bytes
可以節省7字節....
有時候你可能會從將一個值從一個內存地址轉移到另外內存地址,並且要保存所有寄存器:
6) push eax ;1 byte
mov eax, [ebp + xxxx] ;6 bytes
mov [ebp + xxxx], eax ;6 bytes
pop eax ;1 byte
試試push,pop
7) push dword ptr [ebp + xxxx] ;6 bytes
pop dword ptr [ebp + xxxx] ;6 bytes
7.乘法
當eax已經放入被乘數,要乘28h,如何來寫?
1) mov ecx, 28h ;5 bytes
mul ecx ;2 bytes
好一點的寫法如下:
2) push 28h ;2 bytes
pop ecx ;1 byte
mul ecx ;2 bytes
哇這個更好::
3) imul eax, eax, 28h ;3 bytes
intel在新CPU中提供新的指令並不是擺設,需要你的使用.
8.字符串操作
你如何從內存取得一個字節呢?
速度快的方案:
1) mov al/ax/eax, [esi] ;2/3/2 bytes
inc esi ;1 byte
代碼小的方案:
2) lodsb/w/d ;1 byte
我比較喜歡lod因爲他小,雖然速度慢了點.
如何到達字符串尾呢?
JQwerty's method:
9) lea esi, [ebp + asciiz] ;6 bytes
s_check: lodsb ;1 byte
test al, al ;2 bytes
jne s_check ;2 bytes
Super's method:
10) lea edi, [ebp + asciiz] ;6 bytes
xor al, al ;2 bytes
s_check: scasb ;1 byte
jne s_check ;2 byte
選擇哪一個?Super的在386以下的更快,JQwerty的在486以及pentium上更快,體積一樣,選擇由你.
9.複雜一點的...
假設你有一個DWORD表,ebx指向表的開始,ecx是指針,你想給每個doword加1,看看如何作:
1) pushad ;1 byte
imul ecx, ecx, 4 ;3 bytes
add ebx, ecx ;2 bytes
inc dword ptr [ebx] ;2 bytes
popad ;1 byte
可以優化一點,但是好像沒人用:
2) inc dword ptr [ebx+4*ecx] ;3 bytes
一條指令就節省6字節,而且速度更快,更易讀,但好像沒有什麼人用?...why?
還可以有立即數:
3) pushad ;1 byte
imul ecx, ecx, 4 ;3 bytes
add ebx, ecx ;2 bytes
add ebx, 1000h ;6 bytes
inc dwor ptr [ebx] ;2 bytes
popad ;1 byte
優化爲:
4) inc dword ptr [ebx+4*ecx+1000h] ;7 bytes
節省了8字節!
看一下lea指令能爲我們乾點什麼呢?
lea eax, [12345678h]
eax的最後結果是什麼呢?正確答案是12345678h.
假設 EBP = 1
lea eax, [ebp + 12345678h]
結果是123456789h....呵呵比較一下:
lea eax, [ebp + 12345678h] ;6 bytes
==========================
mov eax, 12345678h ;5 bytes
add eax, ebp ;2 bytes
5) 看看:
mov eax, 12345678h ;5 bytes
add eax, ebp ;2 bytes
imul ecx, 4 ;3 bytes
add eax, ecx ;2 bytes
6) 用lea來進行一些計算我門將從體積上得到好處:
lea eax, [ebp+ecx*4+12345678h] ;7 bytes
速度上一條lea指令更快!不影響標誌位...記住下面的格式,在許多地方善用他們你可以節省時間和空間.
OPCODE <SIZE PTR> [BASE + INDEX*SCALE + DISPLACEMENT]
10.下面是關於病毒重定位優化的,懼毒人士請繞行...
下面的代碼你不應該陌生
1) call gdelta
gdelta: pop ebp
sub ebp, offset gdelta
在以後的代碼中我們這樣使用delta來避免重定位問題
lea eax, [ebp + variable]
這樣的指令在應用內存數據的時候是不可避免的,如果能優化一下,我門將會得到數倍收益,打開你的sice或者trw或者ollydbg等調試器,看看:
3) lea eax, [ebp + 401000h] ;6 bytes
假如是下面這樣
4) lea eax, [ebp + 10h] ;3 bytes
也就是說如果ebp後面變量是1字節的話,總的指令就只有3字節
修改一下最初的格式變爲:
5) call gdelta
gdelta: pop ebp
在某些情況下我們的指令就只有3字節了,可以節省3字節,嘿嘿,讓我們看看:
6) lea eax, [ebp + variable - gdelta] ;3 bytes
和上面的是等效的,但是我們可以節省3字節,看看CIH...
11.其他技巧:
如果EAX小於80000000h,edx清0:
--------------------------------------------------
1) xor edx, edx ;2 bytes, but faster
2) cdq ;1 byte, but slower
我一直使用cdq,爲什麼不呢?體積更小...
下面這種情況一般不要使用esp和ebp,使用其他寄存器.
-----------------------------------------------------------
1) mov eax, [ebp] ;3 bytes
2) mov eax, [esp] ;3 bytes
3) mov eax, [ebx] ;2 bytes
交換寄存器中4個字節的順序?用bswap
---------------------------------------------------------
mov eax, 12345678h ;5 bytes
bswap eax ;2 bytes
;eax = 78563412h now
Wanna save some bytes replacin' CALL ?
---------------------------------------
1) call _label_ ;5 bytes
ret ;1 byte
2) jmp _label_ ;2/5 (SHORT/NEAR)
如果僅僅是優化,並且不需要傳遞參數,請儘量用jmp代替call
比較 reg/mem 時如何節省時間:
------------------------------------------
1) cmp reg, [mem] ;slower
2) cmp [mem], reg ;1 cycle faster
乘2除2如何節省時間和空間?
------------------------------------------------------------
1) mov eax, 1000h
mov ecx, 4 ;5 bytes
xor edx, edx ;2 bytes
div ecx ;2 bytes
2) shr eax, 4 ;3 bytes
3) mov ecx, 4 ;5 bytes
mul ecx ;2 bytes
4) shl eax, 4 ;3 bytes
loop指令
------------------------
1) dec ecx ;1 byte
jne _label_ ;2/6 bytes (SHORT/NEAR)
2) loop _label_ ;2 bytes
再看:
3) je $+5 ;2 bytes
dec ecx ;1 byte
jne _label_ ;2 bytes
4) loopXX _label_ (XX = E, NE, Z or NZ) ;2 bytes
loop體積小,但486以上的cpu上執行速度會慢一點...
比較:
---------------------------------------------------------
1) push eax ;1 byte
push ebx ;1 byte
pop eax ;1 byte
pop ebx ;1 byte
2) xchg eax, ebx ;1 byte
3) xchg ecx, edx ;2 bytes
如果僅僅是想移動數值,用mov,在pentium上會有較好的執行速度:
4) mov ecx, edx ;2 bytes
比較:
--------------------------------------------
1) 未優化:
lbl1: mov al, 5 ;2 bytes
stosb ;1 byte
mov eax, [ebx] ;2 bytes
stosb ;1 byte
ret ;1 byte
lbl2: mov al, 6 ;2 bytes
stosb ;1 byte
mov eax, [ebx] ;2 bytes
stosb ;1 byte
ret ;1 byte
---------
;14 bytes
2) 優化了:
lbl1: mov al, 5 ;2 bytes
lbl: stosb ;1 byte
mov eax, [ebx] ;2 bytes
stosb ;1 byte
ret ;1 byte
lbl2: mov al, 6 ;2 bytes
jmp lbl ;2 bytes
---------
;11 bytes
讀取常數變量,試試在指令中直接定義:
-----------------------------
...
mov [ebp + variable], eax ;6 bytes
...
...
variable dd 12345678h ;4 bytes
2) 優化爲:
mov eax, 12345678h ;5 bytes
variable = dword ptr $ - 4
...
...
mov [ebp + variable], eax ;6 bytes
呵呵,好久沒看到這麼有趣的代碼了,前提是編譯的時候支持代碼段的寫入屬性要被設置.
最後介紹未公開指令SALC,現在的調試器都支持...什麼含義呢:就是CF位置1的話就將al置爲0xff
------------------------------------------------------------------
1) jc _lbl1 ;2 bytes
mov al, 0 ;2 bytes
jmp _end ;2 bytes
_lbl: mov al, 0ffh ;2 bytes
_end: ...
2) SALC db 0d6h ;1 byte ;)
------------------------------------------------------------------>over...
2002---hume
轉自:http:// www.PEdiy.com
原作者: Benny/29A
翻譯改寫:hume/冷雨飄心
[注意:這不是鸚鵡學舌的翻譯,我儘量以我的理解傳達原文的本意]
關於代碼優化的文章實在太多了,遺憾的是大部分我都沒有看,儘管他們就擺在我的牀邊(每當我要看的時候就忍不住打哈欠...嘿嘿).這篇文章較短所以翻了一下.
代碼優化的含義:
代碼優化的目標當然是體積小和速度快,但是在通常的情況下二者就象魚和熊掌一樣不能得兼,我們通常尋找的是這二者的折中,究竟應該偏向何方,那就得具體看我們的實際需要.
但有些常識是我們應該牢記的,下面就結合我們最常遇到的具體情況來漫談一下:
1.寄存器清0
我絕對不想再看到下面的寫法:
1) mov eax, 00000000h ;5 bytes
看起來上面的寫法很符合邏輯,但你應當意識到還有更加優化的寫法:
2) sub eax, eax ;2 bytes
3) xor eax, eax ;2 bytes
看看後面的字節數你就應該理解爲什麼要這麼作了,除此之外,在速度上也沒有損失,他們一樣快,但你喜歡xor還是sub呢?我是比較喜歡xor,原因很簡單,因爲我數學不好....
不過Microsoft比較喜歡sub....我們知道windows運行的慢....(呵呵,當然是玩笑這並不是真正原因X-D!)
2.測試寄存器是否爲0
我也不希望看到下面的代碼:
1) cmp eax, 00000000h ;5 bytes
je _label_ ;2/6 bytes (short/near)
[* 注意很多指令針對eax作了優化,你要儘可能多地實用eax,比如CMP EAX, 12345678h (5 bytes)
如果你使用其他寄存器,就是6bytes *]
讓我們看看,簡單的比較指令居然要用7/11 bytes,No No No,試試下面的寫法:
2) or eax, eax ;2 bytes
je _label_ ;2/6 (short/near)
3) test eax, eax ;2 bytes
je _label_ ;2/6 (short/near)
呵呵,只有4/8 bytes,看看我們可節省多少字節啊3/4字節...那麼接下來的問題是你喜歡OR還是TEST呢,就我個人而言,比較喜歡TEST,因爲test不改變任何寄存器,並不向任何寄存器寫入內容,這通常能在pentium機上取得更快的執行速度.
別高興的太早,因爲還有更值得我們高興的事情,假如你要判斷的的是eax寄存器,那麼看看下面的,是不是更有啓發?
4) xchg eax, ecx ;1 byte
jecxz _label_ ;2 bytes
在短跳轉的情況下我們比2)和3)又節省了1字節.oh....___...
3.測試寄存器是否爲0FFFFFFFFh
一些API返回-1,因此如何測試這個值呢?看你可能又要這樣:
1) cmp eax, 0ffffffffh ;5 bytes
je _label_ ;2/6 bytes
hey,不要這樣,寫代碼的時候想一想,於是有了下面的寫法:
2) inc eax ;1 byte
je _label_ ;2/6 bytes
dec eax ;1 byte
可以節省3 bytes並且執行速度會更快.
4.置寄存器爲0FFFFFFFFh
看看假如你是Api的作者,如何返回-1?這樣嗎?
1) mov eax, 0ffffffffh ;5 bytes
看了上面的不會再這麼XXX了吧?看看下面的:
2) xor eax, eax / sub eax, eax ;2 bytes
dec eax ;1 byte
節省一個字!還有寫法:
3) stc ;1 byte
sbb eax, eax ;2 bytes
這有時還可以優化掉1 byte:
jnc _label_
sbb eax, eax ;2 bytes only!
_label_: ...
我們爲什麼用asm呢?這就是原因.
5.寄存器清0並移入低字數值
1) xor eax, eax ;2 bytes
mov ax, word ptr [esi+xx] ;4 bytes
????--->不會吧,這可能是最多初學者的寫法了,我當然原來也是,看了benny的文章之後我決定改寫爲:
2) movzx eax, word ptr [esi+xx] ;4 bytes
收穫2 bytes!
下面的
3) xor eax, eax ;2 bytes
mov al, byte ptr [esi+xx] ;3 bytes
就相應改爲:
4) movzx eax, byte ptr [esi+xx] ;4 bytes
我們應當儘可能利用movzx
5) xor eax, eax ;2 bytes
mov ax, bx ;3 bytes
因爲執行速度不慢並通常能節省字節...
6) movzx eax, bx ;3 bytes
6.關於push,下面是着重代碼體積的優化,因爲寄存器操作總要比內存操作要快.
1) mov eax, 50h ;5 bytes
這樣就小了1 word
2) push 50h ;2 bytes
pop eax ;1 byte
當操作數只有1字節時候,push只有2 bytes,否則就是5 bytes,記住!
下一個問題,向堆棧中壓入7個0
3) push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
push 0 ;2 bytes
佔用14字節,顯然不能滿意,優化一下
4) xor eax, eax ;2 bytes
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
push eax ;1 byte
可以更緊湊,但會慢一點的形式如下:
5) push 7 ;2 bytes
pop ecx ;1 byte
_label_: push 0 ;2 bytes
loop _label_ ;2 bytes
可以節省7字節....
有時候你可能會從將一個值從一個內存地址轉移到另外內存地址,並且要保存所有寄存器:
6) push eax ;1 byte
mov eax, [ebp + xxxx] ;6 bytes
mov [ebp + xxxx], eax ;6 bytes
pop eax ;1 byte
試試push,pop
7) push dword ptr [ebp + xxxx] ;6 bytes
pop dword ptr [ebp + xxxx] ;6 bytes
7.乘法
當eax已經放入被乘數,要乘28h,如何來寫?
1) mov ecx, 28h ;5 bytes
mul ecx ;2 bytes
好一點的寫法如下:
2) push 28h ;2 bytes
pop ecx ;1 byte
mul ecx ;2 bytes
哇這個更好::
3) imul eax, eax, 28h ;3 bytes
intel在新CPU中提供新的指令並不是擺設,需要你的使用.
8.字符串操作
你如何從內存取得一個字節呢?
速度快的方案:
1) mov al/ax/eax, [esi] ;2/3/2 bytes
inc esi ;1 byte
代碼小的方案:
2) lodsb/w/d ;1 byte
我比較喜歡lod因爲他小,雖然速度慢了點.
如何到達字符串尾呢?
JQwerty's method:
9) lea esi, [ebp + asciiz] ;6 bytes
s_check: lodsb ;1 byte
test al, al ;2 bytes
jne s_check ;2 bytes
Super's method:
10) lea edi, [ebp + asciiz] ;6 bytes
xor al, al ;2 bytes
s_check: scasb ;1 byte
jne s_check ;2 byte
選擇哪一個?Super的在386以下的更快,JQwerty的在486以及pentium上更快,體積一樣,選擇由你.
9.複雜一點的...
假設你有一個DWORD表,ebx指向表的開始,ecx是指針,你想給每個doword加1,看看如何作:
1) pushad ;1 byte
imul ecx, ecx, 4 ;3 bytes
add ebx, ecx ;2 bytes
inc dword ptr [ebx] ;2 bytes
popad ;1 byte
可以優化一點,但是好像沒人用:
2) inc dword ptr [ebx+4*ecx] ;3 bytes
一條指令就節省6字節,而且速度更快,更易讀,但好像沒有什麼人用?...why?
還可以有立即數:
3) pushad ;1 byte
imul ecx, ecx, 4 ;3 bytes
add ebx, ecx ;2 bytes
add ebx, 1000h ;6 bytes
inc dwor ptr [ebx] ;2 bytes
popad ;1 byte
優化爲:
4) inc dword ptr [ebx+4*ecx+1000h] ;7 bytes
節省了8字節!
看一下lea指令能爲我們乾點什麼呢?
lea eax, [12345678h]
eax的最後結果是什麼呢?正確答案是12345678h.
假設 EBP = 1
lea eax, [ebp + 12345678h]
結果是123456789h....呵呵比較一下:
lea eax, [ebp + 12345678h] ;6 bytes
==========================
mov eax, 12345678h ;5 bytes
add eax, ebp ;2 bytes
5) 看看:
mov eax, 12345678h ;5 bytes
add eax, ebp ;2 bytes
imul ecx, 4 ;3 bytes
add eax, ecx ;2 bytes
6) 用lea來進行一些計算我門將從體積上得到好處:
lea eax, [ebp+ecx*4+12345678h] ;7 bytes
速度上一條lea指令更快!不影響標誌位...記住下面的格式,在許多地方善用他們你可以節省時間和空間.
OPCODE <SIZE PTR> [BASE + INDEX*SCALE + DISPLACEMENT]
10.下面是關於病毒重定位優化的,懼毒人士請繞行...
下面的代碼你不應該陌生
1) call gdelta
gdelta: pop ebp
sub ebp, offset gdelta
在以後的代碼中我們這樣使用delta來避免重定位問題
lea eax, [ebp + variable]
這樣的指令在應用內存數據的時候是不可避免的,如果能優化一下,我門將會得到數倍收益,打開你的sice或者trw或者ollydbg等調試器,看看:
3) lea eax, [ebp + 401000h] ;6 bytes
假如是下面這樣
4) lea eax, [ebp + 10h] ;3 bytes
也就是說如果ebp後面變量是1字節的話,總的指令就只有3字節
修改一下最初的格式變爲:
5) call gdelta
gdelta: pop ebp
在某些情況下我們的指令就只有3字節了,可以節省3字節,嘿嘿,讓我們看看:
6) lea eax, [ebp + variable - gdelta] ;3 bytes
和上面的是等效的,但是我們可以節省3字節,看看CIH...
11.其他技巧:
如果EAX小於80000000h,edx清0:
--------------------------------------------------
1) xor edx, edx ;2 bytes, but faster
2) cdq ;1 byte, but slower
我一直使用cdq,爲什麼不呢?體積更小...
下面這種情況一般不要使用esp和ebp,使用其他寄存器.
-----------------------------------------------------------
1) mov eax, [ebp] ;3 bytes
2) mov eax, [esp] ;3 bytes
3) mov eax, [ebx] ;2 bytes
交換寄存器中4個字節的順序?用bswap
---------------------------------------------------------
mov eax, 12345678h ;5 bytes
bswap eax ;2 bytes
;eax = 78563412h now
Wanna save some bytes replacin' CALL ?
---------------------------------------
1) call _label_ ;5 bytes
ret ;1 byte
2) jmp _label_ ;2/5 (SHORT/NEAR)
如果僅僅是優化,並且不需要傳遞參數,請儘量用jmp代替call
比較 reg/mem 時如何節省時間:
------------------------------------------
1) cmp reg, [mem] ;slower
2) cmp [mem], reg ;1 cycle faster
乘2除2如何節省時間和空間?
------------------------------------------------------------
1) mov eax, 1000h
mov ecx, 4 ;5 bytes
xor edx, edx ;2 bytes
div ecx ;2 bytes
2) shr eax, 4 ;3 bytes
3) mov ecx, 4 ;5 bytes
mul ecx ;2 bytes
4) shl eax, 4 ;3 bytes
loop指令
------------------------
1) dec ecx ;1 byte
jne _label_ ;2/6 bytes (SHORT/NEAR)
2) loop _label_ ;2 bytes
再看:
3) je $+5 ;2 bytes
dec ecx ;1 byte
jne _label_ ;2 bytes
4) loopXX _label_ (XX = E, NE, Z or NZ) ;2 bytes
loop體積小,但486以上的cpu上執行速度會慢一點...
比較:
---------------------------------------------------------
1) push eax ;1 byte
push ebx ;1 byte
pop eax ;1 byte
pop ebx ;1 byte
2) xchg eax, ebx ;1 byte
3) xchg ecx, edx ;2 bytes
如果僅僅是想移動數值,用mov,在pentium上會有較好的執行速度:
4) mov ecx, edx ;2 bytes
比較:
--------------------------------------------
1) 未優化:
lbl1: mov al, 5 ;2 bytes
stosb ;1 byte
mov eax, [ebx] ;2 bytes
stosb ;1 byte
ret ;1 byte
lbl2: mov al, 6 ;2 bytes
stosb ;1 byte
mov eax, [ebx] ;2 bytes
stosb ;1 byte
ret ;1 byte
---------
;14 bytes
2) 優化了:
lbl1: mov al, 5 ;2 bytes
lbl: stosb ;1 byte
mov eax, [ebx] ;2 bytes
stosb ;1 byte
ret ;1 byte
lbl2: mov al, 6 ;2 bytes
jmp lbl ;2 bytes
---------
;11 bytes
讀取常數變量,試試在指令中直接定義:
-----------------------------
...
mov [ebp + variable], eax ;6 bytes
...
...
variable dd 12345678h ;4 bytes
2) 優化爲:
mov eax, 12345678h ;5 bytes
variable = dword ptr $ - 4
...
...
mov [ebp + variable], eax ;6 bytes
呵呵,好久沒看到這麼有趣的代碼了,前提是編譯的時候支持代碼段的寫入屬性要被設置.
最後介紹未公開指令SALC,現在的調試器都支持...什麼含義呢:就是CF位置1的話就將al置爲0xff
------------------------------------------------------------------
1) jc _lbl1 ;2 bytes
mov al, 0 ;2 bytes
jmp _end ;2 bytes
_lbl: mov al, 0ffh ;2 bytes
_end: ...
2) SALC db 0d6h ;1 byte ;)
------------------------------------------------------------------>over...
2002---hume
轉自:http:// www.PEdiy.com
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.