支持整个程序优化(也称为链接时代码生成)的良好优化编译器并不关心内部函数的调用约定*。它将使用在这种情况下最快/最好的任何调用约定,包括发明自定义调用约定或完全内联函数。
调用约定唯一重要的是构成公共 API 一部分的函数。在那种情况下,__fastcall
可能是一个糟糕的选择。使用Windows 工具链广泛支持的更标准的调用约定,例如__cdecl
or 。对于互操作性来说,这是一个特别糟糕的选择,因为它从未标准化,因此不同供应商的实施方式不同。当您尝试将 DLL 与使用不同工具链(更不用说使用不同语言)编译的应用程序一起使用时,这将成为一场噩梦。__stdcall
__fastcall
当然,当您使用记录为需要__fastcall
约定的 VCL API 时除外。例如,文档说 VCL 类的成员函数使用__fastcall
约定,因此您需要在所有覆盖中使用相同的调用约定。
或者当您需要清理调用者时,例如,支持可变参数。那么你需要__cdecl
.
如果您确实想对内部函数(即那些不属于公共 API 的函数)使用特定的调用约定,那么您真的应该更喜欢使用编译器开关来全局指定它。然后,这将指定要用于其原型未专门覆盖它的所有函数的调用约定。这有几个优点。一方面,它避免了一堆调用约定样板使您的代码混乱。其次,它允许您稍后轻松地进行更改(例如,如果分析显示您最初选择的调用约定是优化器无法解决的瓶颈)。
有趣的__stdcall
是,__cdecl
由于减少了二进制大小,这可能是因为被调用者而不是调用者调整堆栈(并且被调用者比调用者少),但正如您链接的文章提到的那样,__fastcall
可能并不总是比...快__stdcall
. 这篇文章没有涉及任何技术细节,但问题基本上是 32 位 x86 上可用的寄存器数量极其有限。在寄存器中而不是在堆栈中传递值通常是性能上的胜利,但在某些情况下,当函数很大并且用完寄存器时可能会变得悲观,迫使它将参数溢出回堆栈,做双重工作(引起速度损失)并进一步膨胀代码(这会引起缓存损失,并且间接地引起速度损失)。如果值已经在堆栈上,但需要移动到寄存器中以进行函数调用,这也是一种悲观,阻碍了这两个地方的优化潜力。
请注意,当您开始以 64 位 x86 架构为目标时,这一切都变得无关紧要。无论供应商如何,调用约定最终都为所有 Windows 应用程序标准化。x64 调用约定有点类似于__fastcall
,但由于可用寄存器数量较多,因此在那里工作得更好。优化器不需要像在 x86-32 上那样经历尽可能多的扭曲来释放寄存器以传递参数。
*请注意,当我在这里说“内部”函数时,我指的不是特定的访问修饰符,而是指在单个编译域内的函数和/或从未被外部代码调用的函数。