当前位置: 首页 > 知识库问答 >
问题:

优化分支预测:如何推广可以在不同编译器、解释器和硬件预测下运行的代码?

司马昕
2023-03-14

今天,我在一个由if语句引起的紧密循环中遇到了一些缓慢的下降,这让我有些惊讶,因为我期望分支预测能够成功地流水线化特定语句以最小化条件的成本。

当我坐下来思考为什么它没有得到更好的处理时,我意识到我对分支预测是如何处理的知之甚少。我非常了解分支预测的概念和它的好处,但问题是我不知道是谁在实施它,以及他们用什么方法来预测条件的结果。

深入研究,我知道分支预测可以在几个级别上完成:

    < li >带指令流水线的硬件本身 < li>C风格编译器 < li >解释语言的解释器。 < li >像java这样的半编译语言可能做上面的两个和三个。

然而,因为优化可以在许多领域进行,所以我不确定如何预测分支预测。例如,如果我用Java写作,我的条件是在编译时优化,在解释时优化,还是在解释后由硬件优化!?更有趣的是,这是否意味着如果有人使用不同的运行时环境?不同解释器中使用的不同分支预测算法会导致基于条件的紧密循环,根据使用的解释器的不同,条件显示出显著不同的性能

因此,我的问题是,如果软件可以在非常不同的计算机上运行,这可能意味着不同的分支预测,那么如何概括分支预测的优化?如果硬件和解释器可以改变他们的方法,那么分析和使用任何被证明最快的方法都不能保证。让我们忽略C语言,如果你有编译级别的能力来强制执行它,看看解释语言,如果有人仍然需要优化它们中的紧密循环。

无论使用何种解释器,是否有某些通常可以安全做出的假设?人们是否必须深入研究一种语言的复杂规范才能对分支预测做出任何有意义的假设?

共有2个答案

丁韬
2023-03-14

分支预测,如缓存和流水线,是为了让代码运行得更快,通常克服系统中的瓶颈(所有dram都是超慢的廉价dram,X和Y之间的所有总线层等)。

高级语言无法对分支预测进行任何优化,缓存当然可以,有时可以,但分支预测根本不行。为了进行预测,核心必须将分支与执行它的指令放在管道中,跨体系结构和实现不可能找到一条有效的规则。通常甚至在一个体系结构和实现中也不会使用高级语言。

你也可以很容易地在这样的情况下结束,即调整分支预测你去调整缓存或管道或其他你可能想要使用的优化。整体性能首先是特定于应用程序的,然后是特定于该应用程序的东西,而不是通用的东西。

尽管我喜欢在高级语言级别上宣扬和进行优化,但分支预测属于过早优化的范畴。如果尚未启用,只需在核心中启用它,有时它会为您节省几个周期,但大多数情况下不会,并且根据实施情况,它花费的周期可能会比节省的周期多。就像缓存一样,它与命中和未命中有关,如果它猜对了,代码会在更快的ram中更快地到达管道,如果它猜错了,代码会占用总线周期,而这些总线周期本可以由将要运行的代码使用。

缓存通常是一个好处(尽管编写高级代码并不难,以显示它的成本性能而不是节省),因为代码通常在分支之前线性运行一定数量的指令。同样,访问数据的频率也足以克服处罚。分支不是我们每个指令都做的事情,我们分支到哪里没有共同的答案。

您的后端可以尝试调整分支预测,方法是让分支前决策在分支前几个周期发生,但都在管道大小内,并针对获取线或缓存线对齐进行调整。这再次扰乱了核心中其他功能的调整。

连晟
2023-03-14

为了帮助提高分支预测器的性能,尝试构建您的程序,以便条件语句不依赖于明显随机的数据。

这个问题的其他答案之一声称:

没有办法在高级语言上做任何事情来优化分支预测,缓存肯定,有时你可以,但是分支预测,不,根本不是。

然而,这根本不是真的。这个事实的一个很好的例子来自于关于堆栈溢出的一个最著名的问题。

所有分支预测器都通过识别重复代码执行的模式并根据需要使用此信息来预测分支的结果和/或目标来工作。

当用高级语言编写代码时,应用程序程序员通常不需要担心如何优化条件分支。例如,gcc有< code>__builtin_expect函数,它允许程序员指定条件分支的预期结果。但是,即使应用程序程序员确信他们知道特定分支的典型结果,通常也没有必要使用注释。在热循环中使用该指令不太可能有助于提高性能。如果该分支真的有很强的偏向性,即使没有程序员的注释,预测器也能够在大多数情况下正确地预测结果。

在大多数现代处理器上,分支预测器的性能非常好(即使在复杂的工作负载上也优于95%的准确率)。因此,作为微优化,尝试提高分支预测的准确性可能不是应用程序程序员想要关注的事情。通常,编译器将更好地生成适用于其目标特定硬件平台的最佳代码。

但是分支预测程序依赖于识别模式,如果应用程序的编写方式不存在模式,那么分支预测程序的性能就会很差。如果应用程序可以修改为存在模式,那么分支预测器就有机会做得更好。如果你发现分支预测得很差,那么你可以在高级语言的层次上考虑这一点。

 类似资料:
  • 我的代码经常调用具有多个(不可预测的)分支的函数。当我分析时,我发现这是一个小瓶颈,大部分CPU时间用于条件JMP。 考虑以下两个函数,其中原始函数有多个显式分支。 这是一个新函数,我试图在其中删除导致瓶颈的分支。 然而,当我分析新代码时,性能只提高了大约20%,而且调用本身(对mem_funcs数组中的一个func)花费了很长时间。 第二个变量仅仅是一个更隐含的条件吗,因为CPU仍然无法预测将要

  • 如果语句更多地依赖于分支预测,而v表查找更多地依赖分支目标预测,那么

  • 分支目标预测(BTP)与分支预测(BP)不同。我知道BTP会找到分支将跳转到的位置,而BP只是决定可能采取哪个分支。 BTP依赖BP吗,如果BTP不使用BP来预测哪个分支被采用,它怎么可能知道分支的目标呢? 我不明白为什么会有这么大的差异?一旦分支被预测为被占用,找到目标并不像读取指令中的地址一样简单吗?

  • 我最近发现,DRAMs中的比特可以被其中粒子的衰变或宇宙射线随机翻转。我想知道这种错误发生的频率。 不幸的是,我发现的最新统计数据来自1990年(来源),其中指出每128MB内存每月都会发生错误。 由于我找不到任何关于现代ram软错误率的最新统计数据,我尝试用java编写一个程序来测量4GB RAM上的软错误频率。我希望该程序能够检测到分配的4GB内存中的每个软错误,如果它没有以任何方式进行优化的

  • 我目前正在编写一些C99标准库字符串函数的高度优化版本,如< code>strlen()、< code>memset()等,使用带有SSE-2指令的x86-64汇编。 到目前为止,我已经设法在性能方面取得了出色的成绩,但是当我试图进一步优化时,我有时会遇到奇怪的行为。 例如,添加甚至删除一些简单的指令,或者简单地重组一些与跳转一起使用的本地标签会完全降低整体性能。而且在代码方面绝对没有理由。 所以

  • 编辑:我的困惑出现了,因为通过预测哪个分支,你肯定也在有效地进行目标预测?? 这个问题与我关于这个主题的第一个问题有内在联系: 分支预测与分支目标预测 无限循环 语句 或语句 语句的“then”子句结尾(跳过子句) 非虚函数调用 从函数返回 虚函数调用 函数指针调用 语句(如果编译为跳转表) 语句 语句(如果编译成一系列语句) 循环条件测试 和运算符 三元运算符 null 如果我有以下代码: (B