Biz*_*rck 5 c++ vcl c++builder calling-convention c++builder-xe7
我们有很多用C++编写的基于VCL的应用程序.所有VCL方法(下__published类改性剂需要__fastcall调用约定.但是,不管是什么原因,开发商已经加入__fastcall到其他非VCL功能,这些功能private,protected或public.
基于这篇文章,这对我来说毫无意义,因为它不必要地使代码变得复杂,甚至可能是性能损失(尽管可能是可以忽略的).尽管如此,在建议我们在某些地方删除它后,我被告知我们总是这样做,所以要保持一致,这只是一个风格问题.我认为如果没有必要,它实际上会让人感到困惑,所以这是不好的做法.
我的问题是,何时使用__fastcall调用约定是否合适?
一个支持整个程序优化(也就是链接时代码生成)的优秀编译器并不关心内部函数*的调用约定.在这种情况下,它将使用任何调用约定是最快/最好的,包括发明自定义调用约定或完全内联函数.
调用约定唯一重要的是构成公共API一部分的函数.在这种情况下,__fastcall可能是一个糟糕的选择.使用更标准调用约定喜欢__cdecl或者__stdcall,由Windows工具链的广泛支持.__fastcall互操作性是一个特别差的选择,因为它从未标准化,因此不同供应商的实现方式不同.当您尝试将DLL与使用不同工具链编译的应用程序一起使用时,这就变成了一场噩梦,更不用说使用其他语言了.
当然,除非您正在使用记录为需要__fastcall约定的VCL API .例如,文档说VCL类的成员函数使用__fastcall约定,因此您需要在所有覆盖中使用相同的调用约定.
或者当您需要调用者清理时,例如,支持可变参数.那你需要__cdecl.
如果您确实希望对内部函数使用特定的调用约定(即那些不属于公共API的那些),您应该更喜欢使用编译器开关全局指定.然后,这将指定用于原型未明确覆盖它的所有函数的调用约定.这有几个优点.首先,它避免了使用一堆调用约定样板来混乱代码.其次,它允许您以后轻松进行更改(例如,如果分析显示您的原始选择的调用约定是优化程序无法解决的瓶颈).
有趣的__stdcall是,__cdecl由于二进制大小的减少,因为被调用者调整堆栈而不是调用者(并且调用者的数量少于调用者)这一事实可能是有利的,但是正如您链接的文章提到的那样,__fastcall可能并非总是如此.快于__stdcall.本文没有涉及任何技术细节,但问题基本上是32位x86上可用的寄存器数量非常有限.在寄存器中而不是在堆栈中传递值通常是性能上的胜利,但在某些情况下,当函数很大并且寄存器耗尽时,可能会变成悲观,迫使它将参数溢出回堆栈,进行双重工作(唤起速度惩罚)并进一步膨胀代码(这会引起缓存惩罚,并间接地引起速度惩罚).在值已经在堆栈上的情况下,它也是一种悲观,但需要移动到寄存器中以进行函数调用,从而阻碍两个地方的优化潜力.
请注意,当您开始定位64位x86架构时,这一切都变得无关紧要.无论供应商如何,最终都会为所有Windows应用程序标准化调用约定.x64调用约定有点类似__fastcall,但由于可用寄存器数量较多,因此效果更好.优化器不需要通过尽可能多的扭曲来释放寄存器以传递参数,就像它在x86-32上一样.
*请注意,当我在这里说"内部"函数时,我不是指特定的访问修饰符,而是指单个compiland中的函数和/或永远不会被外部代码调用的函数.