⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 代码优化.txt

📁 会变语言实现的一些程序
💻 TXT
📖 第 1 页 / 共 2 页
字号:
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 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -