在阅读Stroustrup的"The C++ Programming Language"时,我在p上看到了这句话.108:
"所使用的语法分析风格通常称为递归下降;它是一种流行且直接的自上而下的技术.在C++这样的语言中,函数调用相对便宜,它也很有效."
有人可以解释为什么C++函数调用很便宜吗?我会对一般性解释感兴趣,即如果可能的话,什么使得函数调用在任何语言中都很便宜.
与大多数其他语言相比,C++中的函数调用很便宜,其原因之一是:C++建立在函数内联的概念之上,而(例如)java建立在一切都是虚拟函数的概念之上.
在C++中,大多数时候你正在调用一个函数,你实际上并没有生成call指令.特别是在调用小函数或模板函数时,编译器很可能会内联代码.在这种情况下,函数调用开销只是零.
即使函数没有内联,编译器也可以对函数的作用做出假设.例如:windows X64调用约定指定调用者应保存寄存器R12-R15,XMM6-XMM15.调用函数时,编译器必须在调用站点生成代码以保存和恢复这些寄存器.但是如果编译器可以证明寄存器R12-R15,XMM6-XMM15未被被调用函数使用,则可以省略这样的代码.调用虚函数时,这种优化要困难得多.
有时内联是不可能的.常见的原因包括函数体在编译时不可用,函数太大.在这种情况下,编译器生成直接call指令.但是,由于调用目标是固定的,因此CPU可以很好地预取指令.尽管直接函数调用很快,但仍然存在一些开销,因为调用者需要在堆栈上保存一些寄存器,增加堆栈指针等.
最后,当使用带有virtual关键字的java函数调用或C++函数时,CPU将执行虚拟call指令.与直接调用的区别在于目标不是固定的,而是存储在内存中.目标函数可能在程序运行期间发生变化,这意味着CPU无法始终在函数位置预取数据.现代CPU和JIT编译器有各种各样的技巧来预测目标函数的位置,但它仍然没有直接调用那么快.
tldr:C++中的函数调用很快,因为C++实现内联,默认情况下使用直接调用虚拟调用.许多其他语言没有像C++一样实现内联,并且默认使用虚函数.
函数调用的成本与从给定范围到另一个范围所需的操作集相关联,即,从当前执行到另一个函数的范围.请考虑以下代码:
void foo(int w) { int x, y, z; ...; }
int main() { int a, b, c; ...; foo(b); ...; }
Run Code Online (Sandbox Code Playgroud)
执行开始main(),您可能会将一些变量加载到寄存器/内存中.当你到达foo()时,一套可使用的变量是不同的:a, b, c值不可达的功能foo(),并且在情况下,你用完了可用的寄存器中,存储的值将不得不溢出到内存中.
寄存器的问题以任何语言出现.但是有些语言需要更复杂的操作才能从范围更改为范围:C++只需将函数所需的任何内容推送到内存堆栈中,保留指向周围范围的指针(在这种情况下,在运行时foo(),您将能够到达win main()的范围的定义.
其他语言必须分配和传递复杂数据,以允许访问周围的范围变量.这些额外的分配,甚至是在周围范围内搜索特定标签,都会大大增加函数调用的成本.
| 归档时间: |
|
| 查看次数: |
2801 次 |
| 最近记录: |