12

我正在用大致以下逻辑编写一段关键代码

if(expression is true){
   //do something with extremely low latency before the nuke blows up. This branch is entered rarely, but it is the most important case
}else{
   //do unimportant thing that doesnt really matter
}

我正在考虑likely()在表达式周围使用宏,所以当它到达重要分支时,我得到最小的延迟。

我的问题是,用法与宏名称建议相反,因为我选择了不太可能的分支进行预取,即重要的分支不太可能发生,但它发生时是最关键的事情。

在性能方面这样做有明显的缺点吗?

4

2 回答 2

9

是的。您通过将不太可能但必须快速的分支标记为可能的分支来欺骗编译器,希望编译器能够使其更快。

这样做有一个明显的缺点——如果你没有写一个好的评论来解释你在做什么以及为什么,一些维护者(可能是你自己)在六个月内几乎肯定会说,“嘿,看起来他将可能的分支放在错误的分支上”并“修复”它。

还有一个不太可能但仍然可能的缺点,即您现在或将来使用的某些编译器版本将执行与您对可能宏的预期不同的事情,并且这些不同的事情不会是您想要的欺骗编译器,你最终会得到这样的代码,每次通过循环,在撤消它之前推测性地花费 100K 美元通过反应器关闭的 90%。

于 2012-06-06T21:44:24.710 回答
4

它与 __builtin_expect(x, 1) 的传统用法完全相反,后者在宏的意义上使用:

#define likely(x) __builtin_expect(x, 1)

我个人认为这是不好的形式(因为您隐晦地将不太可能的路径标记为可能获得性能提升)。但是,您仍然可以标记此优化,因为 __builtin_expect(x) 不会通过声明“likey”路径来假设您的需求 - 这只是标准用途。要执行您想要的操作,我建议:

#define optimize_path(x) __builtin_expect(x, 1)

这将做同样的事情,但不是让代码指责不太可能的路径,而是让代码描述你真正尝试的内容——优化关键路径。

但是,我应该说,如果您计划对 nuke 进行计时 - 您不仅应该手动检查(和计时)已编译的程序集以确保计时正确,而且您还应该使用 RTOS。分支错误预测将产生非常微不足道的影响,以至于在这里几乎没有必要,因为您可以通过简单地拥有更快的处理器或正确地为错误预测的延迟计时来补偿“百万分之一”事件。影响现代计算机时序的是操作系统抢占和调度。如果您需要在非常离散的时间尺度上发生某些事情,您应该将它们安排为实时,而不是大多数通用操作系统所具有的伪实时。分支错误预测通常比在 RT 情况下不使用 RTOS 可能发生的延迟小数百倍。通常,如果您认为分支错误预测可能是一个问题,您可以从时间敏感问题中删除分支,因为分支预测器通常具有复杂且不受您控制的状态。宏之类的“可能”和“不太可能”用于可以从各个区域命中的代码块,具有各种分支预测状态,最重要的是非常频繁地使用。命中这些分支的高频率导致使用它的应用程序(如 Linux 内核)的性能显着提高。如果你只打树枝一次,你 因为分支预测器通常具有复杂且无法控制的状态。宏之类的“可能”和“不太可能”用于可以从各个区域命中的代码块,具有各种分支预测状态,最重要的是非常频繁地使用。命中这些分支的高频率导致使用它的应用程序(如 Linux 内核)的性能显着提高。如果你只打树枝一次,你 因为分支预测器通常具有复杂且无法控制的状态。宏之类的“可能”和“不太可能”用于可以从各个区域命中的代码块,具有各种分支预测状态,最重要的是非常频繁地使用。命中这些分支的高频率导致使用它的应用程序(如 Linux 内核)的性能显着提高。如果你只打树枝一次,你 命中这些分支的高频率导致使用它的应用程序(如 Linux 内核)的性能显着提高。如果你只打树枝一次,你 命中这些分支的高频率导致使用它的应用程序(如 Linux 内核)的性能显着提高。如果你只打树枝一次,你在某些情况下可能会获得 1 纳秒的性能提升,但如果应用程序对时间至关重要,那么您可以采取其他措施来帮助自己获得更大的性能提升。

于 2012-10-05T15:45:57.440 回答