AnT..
82
您可以看到,在从性能角度来看实际上很重要的情况下,例如在一个周期中多次重复调用该函数,性能可能根本不同.
这对于人们来说可能听起来很奇怪,他们习惯于将C代码视为由抽象的C机器执行的,其机器语言与C语言本身密切相关.在这种情况下,"默认情况下"对函数的间接调用确实比直接调用慢,因为它正式涉及额外的内存访问以确定调用的目标.
但是,在现实生活中,代码由真实机器执行,并由优化编译器编译,该编译器非常了解底层机器架构,这有助于它为该特定机器生成最佳代码.在许多平台上,可能会发现从循环执行函数调用的最有效方法实际上会导致直接和间接调用的相同代码,从而导致两者的相同性能.
例如,考虑x86平台.如果我们"直接"将直接和间接调用转换为机器代码,我们可能最终得到类似的东西
// Direct call
do-it-many-times
call 0x12345678
// Indirect call
do-it-many-times
call dword ptr [0x67890ABC]
前者在机器指令中使用立即操作数,并且通常比后者更快,后者必须从某个独立的存储器位置读取数据.
在这一点上,让我们记住,x86架构实际上还有一种方法可以为call指令提供操作数.它在寄存器中提供目标地址.关于这种格式的一个非常重要的事情是它通常比上述两种都快.这对我们意味着什么?这意味着一个好的优化编译器必须并将利用这一事实.为了实现上述循环,编译器将尝试在两种情况下通过寄存器使用调用.如果成功,最终代码可能如下所示
// Direct call
mov eax, 0x12345678
do-it-many-times
call eax
// Indirect call
mov eax, dword ptr [0x67890ABC]
do-it-many-times
call eax
注意,现在重要的部分 - 循环体中的实际调用 - 在两种情况下都完全相同.毋庸置疑,性能几乎完全相同.
有人甚至说,但奇怪的是听起来,这个平台上直接调用(与立即数的调用call)是慢比只要间接调用中提供的操作数的间接调用寄存器(相存储在内存中).
当然,在一般情况下,整个事情并不那么容易.编译器必须处理有限的寄存器可用性,别名问题等.但是像你的例子中那样简单的情况(甚至在更复杂的情况下),上述优化将由一个好的编译器执行并将完全消除循环直接调用和循环间接调用之间的性能差异.当调用虚函数时,这种优化在C++中特别有效,因为在典型的实现中,所涉及的指针完全由编译器控制,使其充分了解别名图片和其他相关内容.
当然,总有一个问题是你的编译器是否足够智能以优化这样的事情......