何时使用`__fastcall` caling约定

时间:2016-05-27 15:16:05

标签: c++ c++builder vcl calling-convention c++builder-xe7

我们有很多用C ++编写的基于VCL的应用程序。所有VCL方法(在__published类修饰符下都需要__fastcall调用约定。但是,无论出于何种原因,开发人员已将__fastcall添加到{{1}的其他非VCL函数中}},privateprotected

基于this article,这对我来说毫无意义,因为它不必要地使代码复杂化,甚至可能是性能损失(尽管可能是可以忽略的)。尽管如此,在建议我们在某些地方删除它后,我被告知我们总是这样做,所以要保持一致,这只是一个风格问题。我认为如果没有必要,它实际上会让人感到困惑,所以这是不好的做法。

我的问题是,何时使用public调用约定?

1 个答案:

答案 0 :(得分:6)

支持整个程序优化(又称链接时代码生成)的优秀编译器并不关心内部函数 * 的调用约定。在这种情况下,它将使用任何调用约定是最快/最好的,包括发明自定义调用约定或完全内联函数。

调用约定唯一重要的是构成公共API一部分的函数。在这种情况下,__fastcall可能是一个糟糕的选择。使用更加标准的调用约定,如__cdecl__stdcall,Windows工具链广泛支持。 __fastcall是互操作性的一个特别糟糕的选择,因为它从未标准化,因此不同供应商的实现方式不同。当您尝试将DLL与使用不同工具链编译的应用程序一起使用时,这就变成了一场噩梦。更多的是用不同的语言。

当然,除非您正在使用记录为需要__fastcall约定的VCL API。例如,文档说VCL类的成员函数使用__fastcall约定,因此您需要在所有覆盖中使用相同的调用约定。

或者当您需要调用者清理时,例如,以支持可变参数。然后你需要__cdecl

如果您确实希望对内部函数使用特定的调用约定(即那些不属于公共API的函数),您应该更喜欢使用编译器开关指定 global 。然后,这将指定用于原型未明确覆盖它的所有函数的调用约定。这有几个优点。首先,它避免了使用一堆调用约定样板来混乱代码。其次,它允许您以后轻松进行更改(例如,如果分析显示您的原始选择的调用约定是优化程序无法解决的瓶颈)。

有趣的是,__stdcall优于__cdecl,因为二进制大小减少,因为被调用者调整堆栈而不是调用者(并且调用者的数量少于调用者) ,但正如您链接的文章所述,__fastcall可能并不总是比__stdcall更快。本文没有涉及任何技术细节,但问题基本上是32位x86上可用的寄存器数量非常有限。在寄存器中而不是在堆栈中传递值通常是性能上的胜利,但在某些情况下,当函数很大并且寄存器耗尽时,可能会变成悲观,迫使它将参数溢出回堆栈,进行双重工作(唤起速度惩罚)并进一步膨胀代码(这会引起缓存惩罚,并间接地引起速度惩罚)。如果值已经已经在堆栈上,那么它也是一种悲观情绪,但需要移入寄存器才能进行函数调用,从而阻碍两个地方的优化潜力。

请注意,当您开始定位64位x86架构时,这一切都变得无关紧要。无论供应商如何,最终都会为所有Windows应用程序标准化调用约定。 x64调用约定有点类似于__fastcall,但由于可用寄存器数量较多,因此效果更好。优化器不需要通过尽可能多的扭曲来释放寄存器来传递参数,就像它在x86-32上一样。

* 请注意,当我在这里说“内部”函数时,我不是指特定的访问修饰符,而是指单个compiland中的函数和/或从未调用过的函数。通过外部代码。