分支预测(Branch Prediction)是现代处理器用来提高CPU执行速度的一种手段, 其对程序的分支流程进行预测, 然后预先读取其中一个分支的指令并解码来减少等待译码器的时间.维基百科上对此的解释是"a strategy in computer architecture design for mitigating the costs usually associated with conditional branches, particularly branches to short sections of code."
分支预测的方法有静态预测和动态预测两类:静态预测方法行为比较简单,如预测永远不转移、预测永远转移(jmp)、预测后向转移等等,它并不根据执行时的条件和历史信息来进行预测,因此预测的准确性不会很高;动态预测方法则根据同一条转移指令过去的转移情况来预测未来的转移情况。
由于程序中的条件分支是根据程序指令在流水线处理后的结果来执行的,所以当CPU等待指令结果时,流水线的前级电路也处于等待分支指令的空闲状态,这样必然出现时钟周期的浪费。如果CPU能在前条指令结果出来之前就预测到分支是否转移,那么就可以提前解码并执行相应的指令,这样就避免了流水线的空闲等待,也就相应提高了CPU的整体执行速度。但另一方面,一旦前条指令结果出来后证明分支预测是错误的, 也就是产生了错误的分支预测,那么就必须将已经装入流水线执行的指令和结果全部清除,然后再装入正确的指令重新处理,这样就比不进行分支预测而是等待结果再执行新指令有额外的周期消耗。
因此,分支预测的错误并不会导致结果的错误,而只是导致流水线的停顿,如果能够保持较高的预测准确率,分支预测就能提高流水线的性能, 换言之, 如果在软件开发过程中, 能够考虑这一特性, 减少甚至移除条件分支(值得一提的是, 条件转移不需要预测, 因此条件转移也远没有产生错误分支的性能代价大), 就能一定程度上提供程序的整体效率, 下面是几种常见的优化策略:
1.避免在循环中嵌套条件分支. 如果可能,将分支移到外部, 使用多个子循环.
do { if (condition_1){ //branch_1 } else if (condition_2){ //branch_2 } else { //branch_3 } //if } while (true); //改进版本 if (condition_1) { do { //branch_1 } while (true); } else if (condition_2) { do { //branch_2 } while (true); } else { do { //branch_3 } while (true); } //if
2.合并分支条件. 此举在某种情况下可以大大降低产生错误分支预测的概率.
if (condition_1 == 0 || condition_2 == 0 || condition_3 == 0) { //branch } //if //改进版本: if ((condition_1 | condition_2 | condition_3) == 0) { //branch } //if
3.移除明显的条件分支, 将执行概率大的条件分支移前.这一条不仅仅有助于规避错误分支带来的性能惩罚, 还减少了不必要的检测分支条件消耗的CPU时钟周期.
尽管现代编译器的优化技术已经十分强大(举个例子, 如果你的产品主要运行在Intel的核心上, 那么使用Intel的编译器并开启优化, 通过VTune性能器来不断改进程序, 你的程序效率将比编译在Any CPU时有很大的飞跃~), 但是比起程序员本身终归略逊, 因此, 程序员或许寄希望于compiler optimization的同时, 更不妨掌握一些优化细节, 对此, 个人认为阅读一些CPU工作原理的书籍颇有裨益~