是否有C ++使用接口(抽象基类)时的运行时性能损失?
Answer 1:
答案很简单:第
龙答:这不是基类或祖先的类在其层次结构,影响它的速度的数量。 唯一的一点是方法调用的成本。
非虚拟方法调用具有成本(但可以被内联)
虚拟方法调用具有成本稍高,因为你需要仰视你怎么称呼它(但是这是一个简单的表查找不是搜索)之前调用该方法。 由于在接口上的所有方法都是通过定义虚拟存在这笔费用。
除非你正在写一些超速度感应应用中,这不应该是一个问题。 额外的清晰度,你将使用一个接口收到通常弥补了任何感知的速度下降。
Answer 2:
使用虚拟调度函数调用不内联
有一种惩罚虚函数这是很容易忘记:虚拟呼叫不能在(公共)的情况,其中内联对象的类型是不知道的编译时间。 如果你的函数小,适用于内联,这个点球可能非常显著,因为你不仅是增加一个调用的开销,但是编译器也被它如何能优化调用函数的限制(它必须承担可能的虚拟功能已经改变了一些寄存器或存储器位置,它不能传播的主叫用户和被叫之间的恒定值)。
虚拟呼叫成本取决于平台
至于比较普通的函数调用的调用开销处罚,答案取决于你的目标平台上。 如果你的目标与在x86 / x64 CPU一台PC,则判为调用虚函数是很小的,因为现代的x86 / x64的CPU可以间接调用执行分支预测。 但是,如果你的目标一个PowerPC或其它一些RISC平台,虚拟呼叫处罚可能是相当显著,因为间接调用从未在某些平台上(参看预测PC / Xbox 360的跨平台开发最佳实践 )。
Answer 3:
有每个虚拟函数调用一个小小的惩罚相比于常规呼叫。 你是不可能,除非你正在做几十万每秒调用来观察差别,而价格往往是值得无论如何付出添加代码清晰。
Answer 4:
当你调用虚函数(通过一个接口说)程序有做功能的一个查找表,看哪个函数调用该对象。 这给出了一个小小的惩罚相比,直接调用该函数。
此外,当您使用一个虚函数,编译器不能内联函数调用。 因此,有可能是一个点球使用虚拟功能对于一些小的功能。 这通常是最大的性能“打”,你可能会看到。 这真的只是一个问题,如果该函数是小且多次调用,从一个循环内说。
Answer 5:
这是适用于某些情况下,另一种选择是编译时多态性与模板。 当你想一个实现选择在节目的开头,然后用它来执行的时间是非常有用的,例如,。 用运行时多态性的例子
class AbstractAlgo
{
virtual int func();
};
class Algo1 : public AbstractAlgo
{
virtual int func();
};
class Algo2 : public AbstractAlgo
{
virtual int func();
};
void compute(AbstractAlgo* algo)
{
// Use algo many times, paying virtual function cost each time
}
int main()
{
int which;
AbstractAlgo* algo;
// read which from config file
if (which == 1)
algo = new Algo1();
else
algo = new Algo2();
compute(algo);
}
同样使用编译时多态性
class Algo1
{
int func();
};
class Algo2
{
int func();
};
template<class ALGO> void compute()
{
ALGO algo;
// Use algo many times. No virtual function cost, and func() may be inlined.
}
int main()
{
int which;
// read which from config file
if (which == 1)
compute<Algo1>();
else
compute<Algo2>();
}
Answer 6:
我不认为,成本比较虚函数调用和直线函数调用之间。 如果你正在考虑使用抽象基类(接口),那么你必须要执行基于动态类的一个对象的几个动作的一个情况。 你必须做出这样的选择不知何故。 一种选择是使用虚拟功能。 另一种方法是对对象的类型的开关,无论是通过RTTI(潜在昂贵的),或添加型()方法将基类(潜在地增加了存储器使用的每个对象的)。 因此,虚函数调用的成本应该是比较替代的成本,而不是无所事事的成本。
Answer 7:
大多数人注意的运行时间延长,这是正确的。
然而,在我的经验,在大型项目,从接口清晰和正确封装的好处迅速弥补速度的增益。 模块化的代码可以换成改进的实现,所以最终的结果是一个大的增益。
您的里程可能会有所不同,这显然取决于你开发的应用程序。
Answer 8:
应该注意的一件事是,虚函数调用的成本可以从一个平台到另一个平台。 在控制台上,他们可能会更明显,因为通常虚函数表调用意味着高速缓存未命中,并且可以拧分支预测。
Answer 9:
需要注意的是多重继承腌多虚表指针的对象实例。 随着G ++在x86上,如果你的类有一个虚拟的方法,并没有基类,你有一个指针的vtable。 如果你有虚方法一个基类,你仍然有一个指针的vtable。 如果你有虚方法两个基类,你必须在每个例如 两个虚函数表的指针。
因此,多重继承(这是在C ++中实现接口的),你付出的对象实例大小基类指针倍大小。 在内存占用的增加可能有间接影响性能。
Answer 10:
在C ++中使用抽象基类一般要求使用一个虚函数表中,所有的接口调用要通过该表进行查找。 相比于原始函数调用的成本是很小的,所以要确保你必须去比速度更快担心它。
Answer 11:
我所知道的唯一的主要区别是,因为你没有使用一个具体的类,内联是(多少?)做起来难。
Answer 12:
我能想到的唯一的事情就是虚拟的方法是稍微慢于非虚方法调用,因为调用要经过的虚方法表 。
然而,这是一个不好的原因搞砸了你的设计。 如果你需要更多的性能,使用更快的服务器。
Answer 13:
至于包含虚拟函数的任何类,使用虚表。 显然,通过调度机制,就像一个虚函数表调用一个方法比直接调用速度较慢,但在大多数情况下,你可以忍受的。
Answer 14:
是的,但没有值得一提的,据我所知。 对性能的影响是因为“间接”你在每个方法调用的。
然而,这真的取决于你所使用的编译器,因为一些编译器不能内联从抽象基类继承的类中的方法调用。
如果你想确保你应该运行自己的测试。
Answer 15:
是的,有一个点球。 这可以提高你的平台上表现的东西是使用非抽象类,没有虚函数。 然后用一个成员函数指针到非虚拟函数。
Answer 16:
我知道这是一种罕见的观点,但即使提的这个问题让我怀疑你把太多想进级结构。 我见过许多系统是有太多的“抽象级别”,这本身使他们容易出现严重的性能问题,方法调用的不是由于成本,但由于趋势进行不必要的调用。 如果这种情况发生在多个层次,这是一个杀手。 看一看