【发布时间】:2012-04-06 21:33:55
【问题描述】:
自从我开始编程以来,我到处阅读,不惜一切代价避免浪费分支。
这很好,尽管没有一篇文章解释我为什么要这样做。当CPU 解码分支指令并决定跳转时究竟会发生什么?是什么“东西”让它比其他指令慢(比如加法)?
【问题讨论】:
标签: optimization architecture language-agnostic compiler-construction cpu
自从我开始编程以来,我到处阅读,不惜一切代价避免浪费分支。
这很好,尽管没有一篇文章解释我为什么要这样做。当CPU 解码分支指令并决定跳转时究竟会发生什么?是什么“东西”让它比其他指令慢(比如加法)?
【问题讨论】:
标签: optimization architecture language-agnostic compiler-construction cpu
分支指令本身并不比任何其他指令慢。
但是,您听说应该避免使用分支的原因是现代 CPU 遵循 pipeline architecture。这意味着有多个顺序指令同时执行。但是,只有在每个周期都能从内存中读取下一条指令时,管道才能充分利用,这反过来意味着它需要知道要读取的条指令。
在有条件的 分支上,它通常不提前知道将采用哪条路径。因此,当这种情况发生时,CPU 必须停止直到决定已经解决,并丢弃分支指令后面的管道中的所有内容。这会降低利用率,从而降低性能。
这就是 branch prediction 和 branch delay slots 这样的东西存在的原因。
【讨论】:
因为 CPU 采用流水线来执行指令,这意味着当上一条指令在某个阶段执行时(例如,从寄存器中读取值),下一条指令将同时执行,但在另一个阶段(例如例如,解码阶段)。非控制指令是可以的,但是当像jmp或call这样的控制指令被执行时,事情就变得复杂了。
由于 CPU 在执行jmp 指令时不知道下一条指令是什么,它使用branch prediction 技术来预测是否会执行分支指令(例如,循环中的分支指令 sn- p 可能会将指令流带回循环头)。
但是,当这种预测失败时,称为branch misprediction,它将影响执行性能。由于分支后的管道必须被丢弃,并从正确的指令重新开始。
【讨论】:
Oli 很好地解释了为什么分支很昂贵:管道和分支预测。但是我想补充一点,您不应该非常担心这个问题,因为现代编译器会优化代码,其中一项优化是减少分支。
您可以在 Microsoft 编译器 here 中阅读有关 C++ 优化的更多信息 - Profile Guided Optimizer 使用运行时信息(即最常用的代码部分)来优化您的代码。加速在 20% 范围内。
其中一个操作是“条件分支优化”,例如 - 假设大部分时间 i 是 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/
【讨论】:
这并不完全相关,但不惜一切代价避免分支的建议对于现代推测性、乱序执行处理器来说简直是无稽之谈。推测执行正是在等待来自内存的数据时为您的处理器提供处理指令的东西。对分支条件的推测是推测执行的全部内容。用算术替换分支实际上会减慢你的程序,所以要小心!更多关于here。
【讨论】: