29

自从我开始编程以来,我已经在各个地方读过,不惜一切代价避免浪费分支。为什么CPU分支指令很慢?

这很好,虽然没有文章解释了为什么我应该这样做。当CPU解码分支指令并决定跳转时究竟发生了什么?什么是“东西”使其比其他指令(如添加)慢?

回答

41

分支指令本来就不比其他任何指令慢。

但是,您听说分支机构应该避免的原因是因为现代CPU遵循pipeline architecture。这意味着有多个顺序指令被同时执行。但是如果流水线能够在每个周期读取内存中的下一条指令,则流水线只能被充分利用,这意味着它需要知道指令要读取的内容。

有条件分支,它通常不会提前知道将采取哪条路径。所以当发生这种情况时,CPU必须停止,直到决策已经解决,并丢弃分支指令后面的管道中的所有内容。这降低了利用率,并因此降低了性能。

这就是存在branch predictionbranch delay slots之类的原因。

+7

+1:对于这个问题同样重要:现代CPU通常具有[barnch预测器](http://en.wikipedia.org/wiki/Branch_predictor)以最小化这个pe性能下降。 – amit 2012-03-22 10:29:41

+0

但有些技术加速了这个称为“分支预测”(http://en.wikipedia.org/wiki/Branch_prediction)的过程。这并不总是奏效,但总体来说效果更好。编辑:我太慢了。 xD – alfa 2012-03-22 10:31:18

+0

只是为了说清楚:拖延管道意味着所有已经预加载的指令都必须卸载。此外,必须恢复任何可能的副作用(通常由于错误预测分支而改变的数据)。所有这些操作都耗费时间和精力。 – NeXuS 2012-03-22 10:32:30

6

由于CPU采用流水线来执行指令,这意味着在某个阶段(例如,从寄存器读取值)执行前一条指令时,下一条指令将同时执行,而在另一个阶段例如解码阶段)。对于非控制指令是可以的,但是当执行如jmpcall的控制指令时,它会使事情变得复杂。

由于CPU不知道执行jmp指令时将执行的下一条指令是什么,它使用branch prediction技术来预测分支指令是否被采用(例如,循环片段中的分支指令可能需要指令流回到循环头)。

但是,如果这种预测失败,称为branch misprediction,则会影响执行性能。由于分支后的流水线必须被丢弃,并从正确的指令开始。

3

Oli给出了一个非常好的解释为什么分支很昂贵:流水线和分支预测。然而我想补充一点,你不应该关心这个问题,因为现代编译器会优化代码,而且一种优化会减少分支。

您可以在Microsoft编译器here中了解更多关于C++优化的信息 - 配置文件引导优化程序使用运行时信息(即代码的哪些部分最常用)来优化代码。加速度在20%的范围内。

一个操作的是“条件转移优化”,例如 - 假设大部分的时间我是6 - 这是更快:

if (i==6) 
{ 
    //... 
} 

else 
{ 
    switch (i) 
    { 
     case 1: // 
     case 2: // 
     //... 
    } 
} 

比:

switch (i) 
{ 
    case 1: // 
    //... 
    case 6: // 
    case 7: // 
} 

这里是一个其他优化的博客文章:http://bogdangavril.wordpress.com/2011/11/02/optimizating-your-native-program/

+0

非常有用的文章!我已经可以在我的代码中看到可以改进的部分了! – user1010005 2012-03-22 11:49:31