自从我开始编程以来,我已经在每个地方阅读,以避免不惜一切代价浪费分支。
这很好,虽然没有一篇文章解释了为什么我应该这样做。当CPU解码分支指令并决定跳转时到底发生了什么?什么是“东西”使它比其他指令(如添加)慢?
答案 0 :(得分:43)
分支指令本身并不比任何其他指令慢。
但是,您听说分支应该避免的原因是因为现代CPU遵循pipeline architecture。这意味着同时执行多个顺序指令。但是如果管道能够在每个周期读取存储器中的下一条指令,那么它只能被充分利用,这反过来意味着它需要知道要读取哪条指令。
在条件分支上,它通常不会提前知道将采用哪条路径。因此,当发生这种情况时,CPU必须停止,直到决策得到解决,并抛弃分支指令后面的管道中的所有内容。这降低了利用率,从而降低了性能。
这就是branch prediction和branch delay slots之类的原因。
答案 1 :(得分:6)
因为CPU采用管道来执行指令,这意味着当某个阶段正在执行前一条指令时(例如,从寄存器读取值),下一条指令将同时执行,但在另一阶段执行(对于例如,解码阶段)。对于非控制指令是可以的,但是当执行jmp
或call
等控制指令时会使事情变得复杂。
由于CPU在执行jmp
指令时不知道下一条指令是什么,因此它使用branch prediction技术来预测是否采用分支指令(例如,分支指令)循环片段可能会将指令流带回循环头。)
但是,当这种预测失败时,称为branch misprediction,它将影响执行性能。由于必须丢弃分支之后的管道,并从正确的指令重新开始。
答案 2 :(得分:3)
Oli非常好地解释了为什么分支是昂贵的:管道和分支预测。我想补充一点,你不应该对这个问题非常关注,因为现代编译器会优化代码,而一个优化就是减少分支。
您可以在Microsoft编译器here中阅读有关C ++优化的更多信息 - Profile Guided Optimizer使用运行时信息(即代码的哪些部分最常用)来优化代码。加速在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/
答案 3 :(得分:0)
这并不完全相关,但是在现代投机,无序执行处理器上,不惜一切代价避免分支的建议简直是胡说八道。推测执行正是在等待内存中的数据时向处理器发出指令进行处理的事情。而对分支条件的推测就是推测执行的全部内容。用算术替换分支实际上会减慢您的程序的速度,所以要当心!有关here的更多信息。