- 注册时间
- 2007-12-28
- 最后登录
- 1970-1-1
- 威望
- 星
- 金币
- 枚
- 贡献
- 分
- 经验
- 点
- 鲜花
- 朵
- 魅力
- 点
- 上传
- 次
- 下载
- 次
- 积分
- 12785
- 在线时间
- 小时
|
发表于 2011-4-1 12:57:20
|
显示全部楼层
8# gxqcn
我来谈一下关于分支预测的话题。
当代CPU均采用pipeline来实现多级流水线,在当前指令的执行阶段,其后的指令,有的已经完成译码,有的已经完成取指等操作,所以顺序执行的指令速度很快,但一旦遇到条件转移,事情就比较复杂了。
早期的CPU(如8086)没有分支预测部件,所以在执行条件转移指令时,如不跳转则速度很快,如跳转则速度很慢,这是因为,当执行跳转指令时,pipeline中的所有内容必须被清除,已经取到的指令,已经完成的译码等操作都白干了。如果你查看8086参考手册,你会发现,对于条件转移指令,如不跳转则仅需4个周期,如跳转则需要16个周期,见本站链接:资料:Intel 和 AMD CPU 资料。
当前的CPU都有分支预测部件,CPU在执行每个分支指令(比较跳转指令对)时,会保存这个指令的跳转情况,当下次执行这条指令时,根据这个指令的跳转情况历史记录,执行跳转概率比较高的那个分支,这个分支的后续指令会提前进入pipeline,这就是分支预测。如果预测正确,则跳转指令的执行速度很快,和普通的指令速度相当。如果预测失败,则必须付出相当的代价。对于当前的CPU,依赖于具体的程序逻辑,分支预测的正确率会有所不同,不过平均起来,分支预测的正确率很高,按照intel的文档,当前的CPU分支预测的正确率一般可达90%以上。
分支有数据依赖分支和非数据依赖分支,对于后者,分支预测的正确率非常高,速度很快,但对于前者速度就相当的慢了。下面的举2个例子来说明。
例子1, 非数据依赖分支
C代码:- int sum(int *p, int len)
- {
- int s=0;
- int i;
- for (i=0;i<len;i++)
- s+=len;
- return s;
- }
复制代码 下面是我写的对应的汇编子程序-
- _sum PROC NEAR
- push ebx
-
- mov ebx, DWORD PTR [esp+4+4] //ebx为p
- mov edx, DWORD PTR [esp+4+8] //edx为len
- xor eax, eax //eax 为返回值是s
- test edx, edx
- jle SHORT this_exit //len<0, 直接返回0, eax为返回值,这里eax=0
- xor ecx, ecx //ecx 为循环变量i,初值为0
-
- //循环部分
- Loop_start:
- mov edx, DWORD PTR [ebx+ecx*4] //edx 为p[ i ]
- add eax, edx //s+= p[ i ]
- inc ecx //i++;
- cmp ecx, DWORD PTR [esp+4+8] // i<len?
- jl Loop_start // i<len 则继续循环
-
- this_exit:
- pop ebx
- ret 0
- _sum ENDP
复制代码 在这个子程序中,共有2处比较跳转指令对
第1处是:
test edx, edx
jle SHORT this_exit //len<0, 直接返回0, eax为返回值,这
第2处是:
cmp ecx, DWORD PTR [esp+4+8] // i-
- int arrayAdd(int array1[], int array2, int len)
- {
- int i,sum,carry;
-
- carry=0;
- for (i=0;i<len;i++)
- {
- sum= array1[ i ]+ array2[ i ] + carry;
- carry=0;
- if (sum>=10000)
- {
- carry=1;
- sum-=10000;
- }
- array1[ i ]=sum;
- }
- return carry;
- }
复制代码我写的等价的汇编代码- _arrayAdd PROC NEAR
- push esi
- push edi
-
- xor eax, eax ; eax:返回值carry
-
- mov edx, DWORD PTR [esp+8+12] //[esp+8+12]: len
- test edx, edx
- jle SHORT this_exit //len<=0,则直接返回0
-
- mov esi, DWORD PTR [esp+8+4] //esi: array1
- mov edi, DWORD PTR [esp+8+8] //edi: array2
- xor ecx,ecx //ecx=0: ecx:循环变量i,
- xor edx,edx //edx=0:edx:carry
-
- loop_start:
- mov eax, dword ptr [esi+ecx*4] //eax =array1[ i ]
- add eax, dword ptr [edi+ecx*4] //eax对应于sum,sum= array1[ i ]+array2[ i ]
- add eax, edx //eax对应与sum, sum= array1[ i ]+array2[ i ]+carry
- xor edx, edx //carry=0
-
- cmp eax, 10000
- jl SHORT next_comp
-
- mov edx, 1 //carry=1
- sub eax, 10000 //sum-=10000
-
- next_comp:
- mov DWORD PTR [esi+ecx*4], eax //array1[ i ]=sum
- add ecx, 1 //i++;
-
- cmp ecx, DWORD PTR [esp+8+12] // i<len?
- jl SHORT loop_start //i<len 则继续循环
-
- mov eax,edx //eax:为返回值
- pop edi
- pop esi
- this_exit:
- ret 0
- _arrayAdd ENDP
复制代码 在这个函数/过程中,共有3处比较跳转指令对
第2处是:
cmp eax, 10000
jl SHORT next_comp
第3处是:
cmp ecx, DWORD PTR [esp+8+12] // i |
|