现代CPU具有广泛的流水线,也就是说,他们正在装载多久,他们实际执行的指令之前必需的指令和数据。
有时候,加载到管道中的数据被无效,管道必须被清除,并用新的数据重新加载。 它需要重填管道的时间可能相当大,并导致性能下降。
如果我用C调用一个函数指针,是管道明智地意识到,在管道中的指针是一个函数指针,并应遵循指针为后续的指令? 或将有一个函数指针,导致管道疏通,并降低性能?
我在C工作,但我想,许多函数调用都是通过V-表,这是即使在C ++更重要。
编辑
@JensGustedt writes:
要为函数调用,您调用必须极其简短的功能一个真正的性能损失。 如果您通过衡量你的代码中观察到这一点,你明确应该重新审视你的设计,以允许内联该调用
不幸的是,这可能是我掉进了陷阱。
我写的目标函数小而快的性能的原因。
但它是通过函数指针引用,以便它可以很容易地与其他功能来代替(只是使指针引用不同的功能!)。 因为我通过一个函数指针指向它,我不认为它可以被内联。
所以,我有一个非常简单的,而不是内联函数。
调用一个函数指针不从调用在C ++中的虚拟方法根本不同,也没有,对于这个问题,是它从一个返回根本不同的。 处理器,在放眼望去,会认识到,通过指针分支快到了,将决定它是否可以在预取管道,安全,有效地解决了指针和走这条道路。 这显然不是遵循正确的相对分支更加困难和昂贵,但是,因为间接分支在现代方案很常见,它的东西,大多数处理器将尝试。
正如奥利说,“清除”管道只会是必要的,如果有误预测在条件分支,这无关与分支是否是偏移或变量地址。 然而,处理器可能有可以预测不同,这取决于分支地址的类型政策 - 一般的处理器是不太可能agressively遵循的,因为一个错误的地址的可能性间接路径关闭条件分支。
在某些处理器间接分支将管道的总是很清楚,至少部分,因为它总是错误预测。 这是专为在无序处理器的情况。
例如, 我跑了一些时机 ,我们的发展在处理器上,比较内联函数调用的开销,对一个直接的函数调用,对间接函数调用(虚拟函数或函数指针;他们在这个平台上是相同的)。
我写了一个微小的函数体并测量它的数百万呼叫的紧密循环,以确定就按这个点球的成本。 在“内联”函数是一个对照组测量功能体(基本上是单负荷运算)的只是成本。 直接函数测量一个正确预测分支的惩罚(因为它是一个静态目标和PPC的预测总能得到这个权利)和函数的序幕。 间接函数测得的的惩罚bctrl
间接分支。
614400000函数调用 :
inline: 411.924 ms ( 2 cycles/call )
direct: 3406.297 ms ( ~17 cycles/call )
virtual: 8080.708 ms ( ~39 cycles/call )
正如你所看到的,直接通话费用15次以上的函数体,和虚拟呼叫( 完全等同于一个函数指针调用 )成本22个周期以上的直接调用。 出现这种情况是大约有多少管线阶段还有的pipline(取指令)和分支ALU结束的开始之间。 因此在这种结构中,间接分支(又名一个虚拟呼叫)导致清晰的22流水线级的100%的时间 。
其他架构可能会有所不同。 你应该把这些决定直接经验测量,或从CPU的流水线的规格,而不是什么处理器“应当”预测的假设,因为实现是如此不同。 在这种情况下,管道发生清晰,因为没有办法的分支预测器知道在哪里bctrl会去,直到它已退休。 至少它可以猜测这是相同的目标作为最后bctrl,这种特殊的CPU甚至没有尝试猜测。
这里没有一个函数指针调用和“正常”的号召,不是额外的间接水平等之间的差异很大。 所以可能有涉及到更大的延迟; 如果目的地址是不是已经在高速缓存或寄存器,那么CPU可能不得不等它从主内存检索。
所以答案是; 是的,管道可以摆摊,但这是正常的函数调用没有什么不同。 和往常一样,机制,如分支预测和乱序执行可以帮助减少罚款。
通过函数指针调用不一定会导致管道清楚,但它可能取决于场景。 最关键的是CPU是否能够有效地预测分支的目标时间提前。
现代“大”乱序核心处理间接调用1的方式大致如下:
- 一旦你几次执行的间接分支,间接分支预测器将尝试预测到分公司将在未来发生的地址。
- 第一个间接分支预测器是非常简单的,能够“预测”只有一个,固定位置的。
- 后来预测,包括那些在最现代的CPU要复杂得多,往往能够预测以及间接跳转的重复模式,并跳转目标与早期的条件或间接分支的方向相关的。
- 如果预测成功,间接调用具有类似于普通直接调用成本,这成本在很大程度上是“脱节”的代码的其余部分(即不参与依赖链 )等的影响代码的最终运行时间很可能是小的,除非电话非常密集。
- 在另一方面,如果预测是不成功的,你得到一个完整的错误预测,类似于一个分支预测错误的方向 。 你不能把固定数量的在该错误预测的成本,因为它依赖于周围的代码,但它通常导致在前端的大约20个循环的气泡,并且在运行时的总成本经常结束相似。
因此,考虑这些基础知识,我们可以在某些特定情况下发生的事情做出一些猜测:
- 一个函数指针总是指向相同的功能几乎总是1中可以很好地预测和成本大约相同的普通函数调用。
- 这几个目标之间交替随机函数指针几乎总是预测失败。 在最好的情况,我们可以希望的预测总是预测什么目标是最常见的,所以在各项指标均在之间的随机统一选择在最坏情况下
N
目标的预测成功率是有界的1 / N
(即变为零, N变到无穷大)。 在这方面,间接分支具有比的条件分支,这通常具有50%2最坏情况的预测错误率更差的最坏情况下的行为。 - 用于与行为的函数指针中间的某个位置的预测速率,例如,有些可预测的(例如,以下的重复图案),将在很大程度上依赖于硬件的细节和预测器的复杂性。 现代英特尔芯片有相当不错的间接预测,但具体细节尚未公开发布。 传统观点认为,他们使用的是一些在塔格间接变型也可用于条件分支预测。
1,将甚至错误预测的单个目标的情况包括:在第一时间(或几次)功能时遇到的,因为预测无法预测目前还没有看到间接调用! 此外,预测资源的CPU中的大小是有限的,所以如果函数指针没有在使用了一段时间,最终预测资源将用于其他分支机构,并在下次调用时,你会遭遇错误预测。
2事实上,一个很简单的条件预测,简单地预测最近经常看到的方向应该有完全随机的分支方向50%的预测率。 要获得超过50%的结果显著更糟的是,你必须设计一个对抗性的算法基本上模型预测,总是选择在模型的相反方向转移。