您的位置:首页 > 其它

理解处理器的“分支预测逻辑”,优化程序性能

2013-10-22 17:51 393 查看
一、什么是“分支预测逻辑”?为什么需要它?

由于处理器通过流水线技术来提高性能,而流水线要求事先知道接下来要执行的具体指令,才能保持流水线中充满待执行的指令。当在程序中遇到分支语句/条件跳转时,问题就出现了,处理器不确定下一条指令是什么,这时就需要进行“分支预测逻辑”来确定哪一条指令进入流水线。

如果预测了一个分支加入流水线,之后确发现它是错误的分支,处理器要回退该错误预测执行的工作,再用正确的指令填充流水线。这样一个错误的预测会严重浪费时钟周期,导致程序性能下降。

举个例子:一个人走到一个岔路口(分支语句),他不知道正确的路是左边还是右边。如果他选择了一条路走了一会后发现不对,他就要再返回到岔路口再走另外一条路,白白耗费了时间。如果他能一次就找到争取的路就好了(分支预测逻辑的必要性)。如何能做到呢?如果他要多次走过岔路口,发现总是右边的路是对的,这时候当再遇到岔路口时,他就可以预测右边的路是正确的(分支预测逻辑的一种原理)。

二、怎样根据“分支预测逻辑”来优化程序性能?

1.一个排序后提升程序性能的例子:

#include <algorithm>
#include <ctime>
#include <iostream>

int main()
{
// 产生随机数数组,随机数在0-255之间
const unsigned arraySize = 32768;
int data[arraySize];

for (unsigned c = 0; c < arraySize; ++c)
data[c] = std::rand() % 256;

// 关键:加上这句后程序运行明显会更快!
//std::sort(data, data + arraySize);

// 测试
clock_t start = clock();
long long sum = 0;

for (unsigned i = 0; i < 100000; ++i)
{
for (unsigned c = 0; c < arraySize; ++c)
{
if (data[c] >= 128)  //条件分支
sum += data[c];
}
}

double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;

std::cout << elapsedTime << std::endl;
std::cout << "sum = " << sum << std::endl;
}


25行是个岔路口,如果随机数是乱序的,分支预测的结果只有50%的正确率,严重影响性能。

然而去掉15行的注释后再执行,由于数组有序,前一部分循环在分支处总是选择同样的分支(<128),这样每到分支处就很容易预测成功,预测成功的概率在90%以上。程序执行速度加快!

还有一种利用位运算避免分支预测的巧妙方法:

把25、26行替换为:

int t = (data[c] - 128) >> (sizeof(int) - 1);
sum += (~t & data[c]);


2.编译器在将代码编译成汇编语言时,处理分支语句时,遇到条件判断的表达时很容易计算的情况,会用条件数据传送指令代替条件控制转移指令,使得无需预测下一条指令,从而改进代码效率。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: