在C++中使用接口的性能损失?

and*_*ykx 42 c++ performance abstract-class virtual-functions

在C++中使用接口(抽象基类)时是否存在运行时性能损失?

Mar*_*ork 46

简答:没有.

长答案:一个类在其层次结构中具有影响其速度的基类或祖先数量.唯一的问题是方法调用的成本.

非虚方法调用具有成本(但可以内联)
虚拟方法调用的成本略高,因为您需要在调用之前查找要调用的方法(但这是一个简单的表查找而不是搜索) .由于接口上的所有方法都是虚拟的,因此存在此成本.

除非您正在编写一些超高速敏感的应用程序,否则这应该不是问题.使用界面可以获得的额外清晰度通常可以弥补任何感知到的速度降低.

  • @Martin:实际上,虚拟方法将在非虚拟方法的情况下内联——即,当可以在编译时确定对象的动态类型并且该方法是可内联的(不是太大+声明)在类定义中或使用“内联”)。 (3认同)
  • 对于具有多个基类的对象的虚方法调用再次比具有单个继承层次结构的对象上的虚方法调用具有稍高的成本,这是值得的. (2认同)

Sum*_*uma 24

使用虚拟分派调用的函数不会内联

对虚函数有一种惩罚很容易忘记:在对象的类型不知道编译时的(常见)情况下,虚拟调用没有内联.如果你的函数很小并且适合内联,那么这个代价可能非常大,因为你不仅增加了调用开销,而且编译器也限制了它如何优化调用函数(它必须假设虚函数可能已经更改了一些寄存器或内存位置,它不能在调用者和被调用者之间传播常量值).

虚拟通话费用取决于平台

至于与正常函数调用相比的调用开销惩罚,答案取决于您的目标平台.如果您的目标是使用x86/x64 CPU的PC,则调用虚拟功能的代价非常小,因为现代x86/x64 CPU可以对间接调用执行分支预测.但是,如果您的目标是PowerPC或其他一些RISC平台,则虚拟呼叫损失可能非常大,因为在某些平台上从不预测间接呼叫(参见PC/Xbox 360跨平台开发最佳实践).

  • 说虚拟调用没有内联是不正确的.每当编译器在编译时确定对象的最终类型时,对该对象的方法调用就是内联的候选者.只有在通过指针到基地调用时才能执行内联. (7认同)
  • @Ghita:即便如此,优化编译器也许能够弄清楚对象的动态类型必须是什么。我的猜测是,大多数编译器可以在“Base* x = new Derived;”中内联对“foo()”的调用。x->foo();`。 (2认同)
  • @j_random_hacker是的,它可以.但在正常的用例中,您通常会将指针传递给基类.值得注意的是,最简单的情况是由编译器处理的. (2认同)

moo*_*dow 9

与常规呼叫相比,每个虚拟功能呼叫有一个小的惩罚.除非您每秒进行数十万次呼叫,否则您不太可能发现差异,并且价格通常值得支付以增加代码清晰度.


Dav*_*ben 5

当您调用虚函数(例如通过接口)时,程序必须在表中查找函数以查看要为该对象调用的函数.与直接调用函数相比,这会带来一点点损失.

此外,当您使用虚函数时,编译器无法内联函数调用.因此,对于某些小功能使用虚函数可能会受到惩罚.这通常是您可能会看到的最大"性能".如果函数很小并且多次调用(例如在循环内),这实际上只是一个问题.