| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2178 人关注过本帖, 1 人收藏
标题:【转载】当代的IA32汇编指令优化手册【zklhp版】(感觉标题不够长)
只看楼主 加入收藏
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
结帖率:100%
收藏(1)
 问题点数:0 回复次数:10 
【转载】当代的IA32汇编指令优化手册【zklhp版】(感觉标题不够长)
当代的IA32汇编指令优化手册

;*****************************************************************************************************************
;作者:zklhp  
;Email:zklhp@
;QQ:493165744
;版权所有    转载请保持完整
;*****************************************************************************************************************


好罢 我是标题党 这个【当代】二字其实很不恰当 因为本文所有的测试均在一个Intel Core 2 Duo T5750 2GHz 上进行 而且还是测试的32位模式 这恐怕不能很好的代表当代的CPU的情况

但是相比那些以386 486来讨论的文章 本文更接近当代CPU的情况 还算是比较实用的

以下的优化技巧来自一些以往的优化文章及本人的积累 文章的数据来自这个测试程序 http://www. 本文对那个程序做了一些修改 但是原理是一样的 测试程序本贴也提供了 呵呵

说明一下 这个测试程序是以nop也就是空指令为基准计算其他指令的用时 下文中所有的指令用时数据都是这个 并不是指令用的周期数 而且 呵呵 测试都是在我上面提到的CPU上进行的

数据并没有用统计方法处理 但是是运行过几次的一个比较公平的数据 只是用来反映指令的快慢情况这种精度足够了

可能会不断补充的 先发写出来的罢 期待其他CPU的测试数据 欢迎拍板砖。。。

寄存器清零

程序代码:
   
    mov eax,0
    ;长度:5字节 用时:3.77
    ;一条占用5字节而且用时最多的写法 出现在程序中是找喷的
   
    xor eax,eax
    ;长度:2字节 用时:1.58
   
    sub eax,eax
    ;长度:2字节 用时:1.57
   


肯定要用xor或sub法

PS:很有意思的是 某些时候sub法用时小于1 较小概率随机出现 原因未知 有待进一步测试

寄存器赋1

程序代码:
   
    mov eax,1
    ;长度:5字节 用时:3.79
   
    sub eax,eax
    inc eax
    ;长度:3字节 用时:2.51
    ;两条指令比一条指令要好
   
    xor    eax,eax
    inc eax
    ;长度:3字节 用时:2.52
   
    mov eax,edx    ;已知为0的寄存器
    inc eax
    ;长度:3字节 用时:2.52
   
    lea eax,ds:[1]
    ;长度:6字节 用时:4.65
    ;也是一种写法
   


寄存器赋-1

程序代码:
   
    mov eax,0FFFFFFFFH
    ;长度:5字节 用时:3.79
    ;已经没有人这么写了罢
   
    xor eax,eax
    dec eax
    ;长度:3字节 用时:2.49
   
    stc
    sbb eax,eax
    ;长度:3字节 用时:2.50
    ;比较怪异的写法 貌似没有速度上的收获
   


寄存器加1

程序代码:
   
    add eax,1
    ;长度:3字节 用时:3.04
   
    inc eax
    ;长度:1字节 用时:3.01
    ;优势在指令长度 速度不明显
   
    lea eax,[eax+1]
    ;长度:3字节 用时:2.96
   


可以认为三者的速度是一样的 但是长度3字节的写法和一字节的inc比差了点
有兴趣的可以用统计的方法看看lea是不是比普通的写法快

寄存器加立即数

程序代码:
   
    add eax,1234
    ;长度:5字节 用时:3.86
   
    adc eax,1234
    ;长度:5字节 用时:6.07
   
    lea eax,[eax+1234]
    ;长度:6字节 用时:4.61
   


lea在这里不好用

寄存器与0比

程序代码:
   
    test eax,eax
    ;长度:2字节 用时:1.56
   
    or eax,eax
    ;长度:2字节 用时:3.01
    ;
   
    cmp eax,0
    ;长度:3字节 用时:2.26
    ;谁会用这个呢。。
   


出现立即数的写法一贯不好

寄存器与-1比

程序代码:
   
    cmp eax,0ffffffffh
    ;长度:3字节 用时:2.33
    ;编译出来是cmp eax,0ffh
   
    inc eax
    dec eax
    ;长度:2字节 用时:5.96
    ;比较的时候肯定会接一个跳转指令 但我这个测试方法没法做这样的测试 所以就比较不加跳转的 都少一个跳转的情况下可以比
    ;这个写法中间有一个je的 以上的比较测试都有跳转指令的
   


寄存器赋一个字节的立即数(感觉这个描述很别扭)

程序代码:
   
    mov eax,88
    ;长度:5字节 用时:3.82
   
    push 88
    pop eax
    ;长度:3字节 用时:7.72
    ;慢但是在这种情况下能省字节
    ;这是立即数是字节时的一种小技巧
   


乘以一个数

程序代码:
   
    mov ebx,8888
    mul ebx
    ;长度:7字节 用时:13.26
   
    mov ebx,8888
    imul ebx
    ;长度:7字节 用时:13.58
   
    imul eax,eax,8888
    ;长度:6字节 用时:9.26
    ;IMUL Reg, Reg/Mem ;80386+ Reg1 ← Reg1×Reg2  或  Reg1 ← Reg1×Mem 各操作数的位数要一致
   
    shl eax,3
    ;长度:3字节 用时:3.06
    ;用位移代替乘法能大大提高速度 但是只能是乘2的次方的时候才能用
    ;这里用来做个对照
   


要优先使用 IMUL Reg, Reg/Mem

shl的用法

程序代码:
   
    shl eax,2
    ;长度:3字节 用时:3.04
   
    shl eax,1
    shl eax,1
    ;长度:4字节 用时:6.01
   


我曾经这样写过 弄巧成拙

xchg的好处

程序代码:
   
    xchg ebx,edx
    ;长度:2字节 用时:6.03
   
    mov eax,ebx
    mov ebx,edx
    mov edx,eax
    ;长度:6字节 用时:6.16
   
    xor ebx,edx
    xor edx,ebx
    xor ebx,edx
    ;长度:6字节 用时:9.10
   
    push ebx
    push edx
    pop edx
    pop ebx
    ;长度:4字节 用时:15.19
   


eax=ecx*4+3

程序代码:
   
    mov eax,ecx
    shl eax,2
    add eax,3
    ;长度:8字节 用时:6.44
   
    lea eax,[ecx*4+3]
    ;长度:7字节 用时:5.38
   


lea应该这么用

测试用的程序 代码+可执行文件+MasmPlus工程
InsBMT.zip (11.76 KB)


★★★★★★★★★★★★★★★★★★★★★★★★★★以上是12月18日的★★★★★★★★★★★★★★★★★★★★★★★★★★

寄存器加2

程序代码:
   
    add eax,2
    ;长度:3字节 用时:3.04
   
    inc eax
    inc eax
    ;长度:2字节 用时:5.99
   


差强人意 省了1字节 虽然这点时间和这点空间都不大、、、

寄存器和内存比较时的顺序问题

程序代码:
   
    cmp eax,DWORD ptr [buffer]    ;也就是一个内存变量啦
    ;长度:6字节 用时:4.63
   
    cmp DWORD ptr [buffer],eax
    ;长度:6字节 用时:4.70
    ;有人说能省一个字节的。。
   
    cmp ebx,DWORD ptr [buffer]
    ;长度:6字节 用时:4.60
   
    cmp DWORD ptr [buffer],ebx
    ;长度:6字节 用时:4.56
   


这里也出现了和第一个一样的现象

▲▲▲注意▲▲▲

关于这个现象我更正一下 上面的描述其实是错误的或者说是与事实不符的 上面的我就不改了 以这个为准。。

我观察到的现象是 某些时候运行这个程序得到的

相对nop的比值:4.647061

在某些时候会明显偏小 比如这个cmp可能会出现2点几的数

开始我就把它描述成了【某些时候sub法用时小于1】 理解成了指令在某些时候会运行的特别快 用时短

但是明显不对啊 我这里相当于执行了1G个指令 如果有偶然的偏差对于整体数据的影响应该是微乎其微的 所以应该跟指令没关系

我觉得可能的原因是某些东西拖慢了nop的执行速度 唉 我当时没看上面两个数。。 我再多试几次看看能不能重现这个问题罢。。。

导致这个的原因有可能有好多喽 操作系统或运行着的其他程序带来的影响 或者 因为我是在MasmPlus按运行执行的 是不是和这个有关系呢 或者是代码的问题 抑或是除法算出来不对? 都可能

这其实就是很多人说的我这种测试方式带来的弊端 不过罢 可以用各种方式接近真实结果 消除这些误差 我想的一个方法是可以看下面的例子

对了 看来测试需要重启电脑并关闭其他程序才行 我上面的都没这么做。。。

inc eax 和 lea eax,[eax+1] 哪个快?

不需要对代码做大的修改 用批处理里面的循环+cvs+Excel来研究一下就好了。。

程序代码:
    @echo off
    for /l %%i in (1,1,50) do console >> 50.csv
    pause


批处理里for的用法可以用for /?来看 呵呵

代码改输出格式 变成这样(突然发现程序的注释写错了 你们明白就好了)

程序代码:
    invoke printf,CTXT('%lu,'),[dqTime1]
    invoke printf,CTXT('%lu,'),[dqTime2]
    invoke printf,CTXT('%f',0dh,0ah),[fTimes]


改成这样让它输出成 基准用时,指令用时,比值 的形式 配合上面的 可以导入到excel里用统计的方法看到底哪个快

50次测试 数据不传了 直接放结论

inc eax                2.98846542±0.021641592        (平均值±标准差)
lea eax,[eax+1]        2.9813342±0.016966188        (平均值±标准差)

不用什么显著性差异也可以知道 没区别 一样快。。



[ 本帖最后由 zklhp 于 2011-12-18 17:27 编辑 ]
收到的鲜花
  • zaixuexi2011-12-18 22:49 送鲜花  50朵   附言:原创内容
搜索更多相关主题的帖子: Intel Email 优化 手册 而且 
2011-12-18 10:42
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
自己顶、、、
2011-12-18 10:43
zaixuexi
Rank: 12Rank: 12Rank: 12
来 自:上海
等 级:火箭侠
威 望:8
帖 子:858
专家分:3233
注 册:2010-12-1
收藏
得分:0 
我也翻看了386,486的instruction cycle,单周期指令的也不少mov reg,imm已经是单周期了,你这个用时是ns的1/2GHZ=1/10-9吧,描述指令的快慢绝对没问题,用这个来指导优化程序代码,好象不太适合,主要对速度有影响的还是多周期指令,mul,div,10倍数量级以上的,还有跳转指令,会刷流水线的,其实,差几个时钟周期的指令,一般也不用优化了,CPU HZ这么高,很难体现出来的
期待版主后面的文章.

技术问题,请不要以短消息方式提问
2011-12-18 16:00
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
你这个用时是ns的1/2GHZ=1/10-9吧,描述指令的快慢绝对没问题,用这个来指导优化程序代码,好象不太适合

没明白啥意思。。。

跳转指令的话 没法用这个模型来统计
2011-12-18 16:53
zaixuexi
Rank: 12Rank: 12Rank: 12
来 自:上海
等 级:火箭侠
威 望:8
帖 子:858
专家分:3233
注 册:2010-12-1
收藏
得分:0 
3.77, 1.59, 1.57单位是多少?f=2GHz,T=1/f=0.5*10的负9次,0.5纳秒,基于nop的instruction cycle, 3.77*0.5ns,3.77是这个意思?
你的优化手册的用途在哪里?是对项目优化么?我觉得编译器可以做掉的
但是跳转指令编译器做不到的,只要分支预测错误,就会重刷全部的流水线的,我是指这个

技术问题,请不要以短消息方式提问
2011-12-18 17:06
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
以下是引用zaixuexi在2011-12-18 17:06:55的发言:

3.77, 1.59, 1.57单位是多少?f=2GHz,T=1/f=0.5*10的负9次,0.5纳秒,基于nop的instruction cycle, 3.77*0.5ns,3.77是这个意思?
你的优化手册的用途在哪里?是对项目优化么?我觉得编译器可以做掉的
但是跳转指令编译器做不到的,只要分支预测错误,就会重刷全部的流水线的,我是指这个

第一个貌似就是这个意思

用途么 其实就是把386 486时代的东西拿过来用现在的CPU验证一下而已

下面的那个不知道弄了

2011-12-18 17:30
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
哦 我又更新了 不过不知道为啥现在编辑帖子要打验证码
2011-12-18 17:30
zaixuexi
Rank: 12Rank: 12Rank: 12
来 自:上海
等 级:火箭侠
威 望:8
帖 子:858
专家分:3233
注 册:2010-12-1
收藏
得分:0 
以下是引用zklhp在2011-12-18 17:30:38的发言:

哦 我又更新了 不过不知道为啥现在编辑帖子要打验证码
好象我没遇到过要打验证

技术问题,请不要以短消息方式提问
2011-12-18 17:43
zjsxwc
Rank: 7Rank: 7Rank: 7
等 级:黑侠
威 望:1
帖 子:252
专家分:601
注 册:2011-1-20
收藏
得分:0 
lea的用法学习了,lea eax,[eax+8*ecx+122]

The tools I recommended:
GUI: CSharp(VS), QT;    Core Code: Plain C (Tiny C Compiler);    Web: Python, JavaScript;    Android: Java;    Embedded System: ASM&C (Linux)
2011-12-20 22:11
zjsxwc
Rank: 7Rank: 7Rank: 7
等 级:黑侠
威 望:1
帖 子:252
专家分:601
注 册:2011-1-20
收藏
得分:0 
Intel Core 2是64位的cpu吧,剩余的8个64位寄存器R8,R9....R15,没利用。。。。。。。。

The tools I recommended:
GUI: CSharp(VS), QT;    Core Code: Plain C (Tiny C Compiler);    Web: Python, JavaScript;    Android: Java;    Embedded System: ASM&C (Linux)
2011-12-20 22:14
快速回复:【转载】当代的IA32汇编指令优化手册【zklhp版】(感觉标题不够长)
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.022880 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved