【问题标题】:How does "goto" statements affect the "branch prediction" of the CPU?“goto”语句如何影响 CPU 的“分支预测”?
【发布时间】:2023-03-12 13:46:02
【问题描述】:

为了了解更多关于 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 跳转到的位置,以便能够预取那里的指令。

你知道吗?

【问题讨论】:

  • goto 是无条件跳转,与分支预测100%无关。分支预测适用于条件分支:ifwhileforvirtual 和函数指针..
  • @Mooing 但是分支预测如何与条件分支中包含的无条件跳转交互?这是我的问题。
  • 分支预测像对待其他指令一样对待无条件跳转:它完全忽略它。我真的不明白这个问题。也许您对 CPU 管道的工作方式感到困惑,只是使用了错误的术语? 所说的“分支预测器”是什么意思?
  • 我的理解是,“分支预测器”是确定在实际执行之前应该将哪些指令放入 CPU 管道的东西。我真的不知道该怎么解释。就像我说的,我还不太了解这个主题。
  • @user3787875:哦!我是一个很好的猜测!您正在描述“CPU管道”的“指令获取”阶段。 “分支预测器”是指令提取的很大一部分,但显然您是在询问整个提取阶段。好的,我们现在在同一个页面上。

标签: c optimization cpu goto branch-prediction


【解决方案1】:

由于“流水线”和类似活动, 分支指令实际上可以放置几条指令 在实际分支发生的位置之前。 (这是在编译器中找到的分支预测逻辑的一部分)。

goto 语句只是一个跳转指令。

附带说明: 给定结构化编程概念, 代码清晰度、可读性、可维护性考虑等; 永远不要使用“goto”语句。

在大多数 CPU 上, 任何跳转/调用/返回类型的指令都会刷新预取缓存 然后从新位置重新加载该缓存,如果新位置 尚未在缓存中。

注意:对于小循环, 它将始终包含“至少”一个跳转指令, 许多 CPU 都有一个程序员可以利用的内部缓冲区 使小循环只执行一个预取序列 因此执行速度提高了许多数量级。

【讨论】:

  • 结构化编程中是否应该使用goto,不是本题的主题。当结构化编程工具不支持这种情况时,总是可以使用 Goto,例如在没有 try..catch 概念的 C 中的错误处理/清理中。
  • 您已通过缩进 4 个空格将答案的文本标记为 CODE。不要。
  • “永远不要使用'goto'语句”:这是一个教条式的语句。在某些情况下,goto 是最佳选择。 (Goto 被认为是有害的。)
  • 有人声称不应使用goto,但从未联系过CPSLR parsers
【解决方案2】:

无条件分支对于分支预测器来说不是问题,因为分支预测器不必预测它们。

它们为推测指令获取单元增加了一点复杂性,因为分支(以及其他改变指令指针的指令)的存在意味着指令并不总是以线性顺序获取。当然,这也适用于条件分支。

请记住,分支预测和推测执行是不同的东西。您不需要分支预测来进行推测性执行:您可以假设从未采用分支的情况下推测性地执行代码,并且如果您确实采用了分支,请取消该分支之外的所有操作。在无条件分支的情况下,这将是一件特别愚蠢的事情,但它会使逻辑保持良好和简单。 (IIRC,这就是第一个流水线处理器的工作方式。)

(我猜你可以在没有推测执行的情况下进行分支预测,但实际上并没有什么意义,因为分支预测器没有人可以告诉它的预测。)

所以是的,分支——条件和无条件的——增加了指令获取单元的复杂性。没关系。 CPU 架构师是一些非常聪明的人。

编辑:在糟糕的过去,人们观察到使用goto 语句可能会对当时的编译器优化代码的能力产生不利影响。这可能是你想的。现代编译器要聪明得多,通常不会被goto 吓到。

【讨论】:

  • 具有推测性获取但没有推测性执行的分支预测可以说是一个胜利。 (在“我猜……但真的没有意义”中)。
  • @danfuzz 这是一个很好的观点。在这里,我假设指令获取是执行的一部分,但是对于 OOO 和超标量执行等功能,我认为这过于简单了。
猜你喜欢
  • 2015-01-05
  • 2020-02-12
  • 1970-01-01
  • 2020-07-16
  • 2014-02-26
  • 2019-08-20
  • 1970-01-01
  • 2021-02-27
  • 1970-01-01
相关资源
最近更新 更多