连载:编写高效代码(11) 尽量减少分支
2011-12-09 22:39
471 查看
我们在介绍处理器时,已经知道了,现在的处理器都是流水线结构,if和switch等语句会带来跳转,而跳转会打乱流水线的正常执行,影响程序的执行效率。
下面这段代码,把奇数赋一个值,把偶数赋一个值,可以用这种方式实现:
如果改成如下这种形式就更好了:
将最可能进入的分支放在 if中,而不是else中
Intel处理器有分支预测单元,第一次进入一个分支时,由于没有历史信息可供参考,是否跳转取决于Static Predictor(静态预测器)的预测策略,通常Static Predictor的策略是:向下跳转预测为不跳转,向上跳转预测为跳转。根据这个特性,我们在写if else语句时,也要注意。且看下面这段代码是否合适?
我们来看看汇编语言:
下面这段代码,把奇数赋一个值,把偶数赋一个值,可以用这种方式实现:
for(i=0; i<100; i++) { if(i%2 == 0) { a[i] = x; } else { a[i] = y; } }
如果改成如下这种形式就更好了:
for(i=0; i<100; i+=2) { a[i] = x; a[i+1] = y; }
将最可能进入的分支放在 if中,而不是else中
Intel处理器有分支预测单元,第一次进入一个分支时,由于没有历史信息可供参考,是否跳转取决于Static Predictor(静态预测器)的预测策略,通常Static Predictor的策略是:向下跳转预测为不跳转,向上跳转预测为跳转。根据这个特性,我们在写if else语句时,也要注意。且看下面这段代码是否合适?
int a = -5; int b = 0; if (a > 0) { b = 1; } else { b = 2; }
我们来看看汇编语言:
4: int a = -5; 00401028 mov dword ptr [ebp-4],0FFFFFFFBh 5: int b = 0; 0040102F mov dword ptr [ebp-8],0 6: if (a > 0) 00401036 cmp dword ptr [ebp-4],0 0040103A jle main+35h (00401045) 7: { 8: b = 1; 0040103C mov dword ptr [ebp-8],1 9: } 10: else 00401043 jmp main+3Ch (0040104c) 11: { 12: b = 2; 00401045 mov dword ptr [ebp-8],2 13: }读者没必要管这么一大串汇编语言是什么意思,只需要知道jle是个条件跳转指令就可以了,它跳到地址00401045处,是向下跳,根据Static Predictor的策略,它被预测为不跳转,处理器会从0040103C地址处开始取下一条指令。再看看实际的执行情况,a<0,执行else这个分支,于是处理器发现取错了地址,又要从头来过,白白浪费了大量时间。可见,执行概率高的分支,应该放在if分支中。
相关文章推荐
- 编写高效代码(11) 尽量减少分支
- 编写高效代码(5) 尽量减少分支
- 连载:编写高效代码(7) 减少函数调用——不要老打断我
- 连载:编写高效代码(9) 减少处理器不擅长的操作——不要逼我做我不喜欢的事情
- 编写高效代码(9) 减少处理器不擅长的操作——不要逼我做我不喜欢的事情
- 连载:编写高效代码(4)—算法领域的牛人们
- 连载:编写高效代码(3)——使用更快的算法
- 连载:编写高效代码(5)——选用合适的指令
- 连载:编写高效代码(6)——降低数据精度
- 连载:编写高效代码(13) 数据对齐访问
- 编写高效Lua代码的方法 - 4 - 减少,重用,回收
- 连载:编写高效代码(12) 优化内存访问——别让包袱拖垮了你
- 编写高效代码(7) 减少函数调用——不要老打断我
- 编写高效代码(1) 减少函数调用——不要老打断我
- 编写高效代码(3) 减少处理器不擅长的操作——不要逼我做我不喜欢的事情
- 连载:编写高效代码(8) 空间换时间——我们总是在走,却忘了停留
- 编写高效代码(1) 减少函数调用——不要老打断我
- 连载:编写高效代码(14) 程序、数据访问符合Cache的时间、空间局部性
- 连载:编写高效代码(1)—编写高效代码的意义
- 连载:编写高效代码(10) 在精度允许的条件下,将浮点数定点化