为了了解更多关于CPU和代码优化的信息,我已经开始学习汇编编程。我还读过一些聪明的优化,比如CPU为加速自己而做的"分支预测"。
我的问题可能看起来很愚蠢,因为我还不太了解这个主题。
我有一个非常模糊的记忆,我在某处(在互联网上)读到goto
语句会降低程序的性能,因为它不能很好地与 CPU 中的分支预测配合使用。然而,这可能只是我编造的东西,并没有真正阅读。
我认为这可能是真的。
我希望这个例子(伪C)能澄清为什么我认为是这样:
int function(...) {
VARIABLES DECLARED HERE
if (HERE IS A TEST) {
CODE HERE ...
} else if (ANOTHER TEST) {
CODE HERE ...
} else {
/*
Let us assume that the CPU was smart and predicted this path.
What about the jump to `label`?
Is it possible for the CPU to "pre-fetch" the instructions over there?
*/
goto label;
}
CODE HERE...
label:
CODE HERE...
}
对我来说,这似乎是一项非常复杂的任务。这是因为这样 CPU 将需要查找goto
跳转到的位置,以便能够在那里预获取指令。
你知道吗?
无条件分支对于分支预测器来说不是问题,因为分支预测器不必预测它们。
它们增加了推测指令获取单元的复杂性,因为分支(以及更改指令指针的其他指令)的存在意味着指令并不总是以线性顺序获取。当然,这也适用于条件分支。
请记住,分支预测和推测执行是不同的东西。您不需要分支预测来推测执行:您可以推测执行代码,假设分支永远不会被占用,如果您确实采用分支,请取消该分支之外的所有操作。在无条件分支的情况下,这将是一件特别愚蠢的事情,但它会保持逻辑的美好和简单。(IIRC,这是第一个流水线处理器的工作方式。
(我想你可以在没有推测性执行的情况下进行分支预测,但它实际上没有意义,因为分支预测器不会有人告诉它的预测。
所以是的,分支 - 有条件和无条件 - 增加了指令获取单元的复杂性。没关系。CPU架构师是一些非常聪明的人。
编辑:回到糟糕的过去,人们观察到使用goto
语句可能会对当时编译器优化代码的能力产生不利影响。这可能就是你在想的。现代编译器要聪明得多,并且通常不会对goto
太吃惊。
由于"流水线"和类似活动,分支指令实际上可以放置几条指令在实际分支发生的位置之前。(这是编译器中分支预测逻辑的一部分)。
goto 语句只是一个跳转指令。
作为旁注:给定结构化编程概念,代码清晰度、可读性、可维护性考虑等;切勿使用"goto"语句。
在大多数 CPU 上,任何跳转/调用/返回类型的指令都将刷新预取缓存然后从新位置重新加载该缓存,如果新位置尚未在缓存中。
注意:对于小循环, 它将始终包含"至少"一个跳转指令, 许多CPU都有一个内部缓冲区,程序员可以利用它 使小循环仅执行一个预取序列 因此执行速度提高了许多数量级。