使用gprof分析我的C ++代码,我发现我的大部分时间花在一遍又一遍地调用一个虚拟方法上。该方法本身很短,如果它不是虚拟的,可能会内联。
有什么方法可以加快这一点而不是将其重写为不是虚拟的?
答案 0 :(得分:9)
你确定时间与电话有关吗?可能是成本本身的功能吗?如果是这种情况,简单地内联可能会使您的分析器中的功能消失,但您不会看到很多加速。
假设实际上是进行如此多虚拟调用的开销,那么在不使虚拟非虚拟的情况下,您可以做的事情是有限制的。
如果电话有时间/标志之类的早期电话,那么我会经常使用两级方法。检查内联非虚拟调用,只在必要时调用特定于类的行为。
E.g。
class Foo
{
public:
inline void update( void )
{
if (can_early_out)
return;
updateImpl();
}
protected:
virtual void updateImpl( void ) = 0;
};
答案 1 :(得分:6)
是时间花在实际的函数调用中还是在函数本身中?
虚拟函数调用 明显慢于非虚拟调用,因为虚拟调用需要额外的解除引用。 (谷歌如果你想阅读所有毛茸茸的细节,可以'vtable'。)更新:事实证明Wikipedia article对此并不差。
“明显地”在这里,意味着一些指令如果它占用总计算的很大一部分,包括在被调用函数中花费的时间,这听起来像是一个考虑非虚拟化和内联的奇妙场所。
但是在近20年的C ++中,我认为我从未见过这种情况真的发生过。我很乐意看到代码。
答案 2 :(得分:6)
如果虚拟呼叫确实是瓶颈,请尝试CRTP。
答案 3 :(得分:5)
请注意,“虚拟”和“内联”不是对立的 - 一种方法可以是两者。如果编译器可以在编译时确定对象的类型,编译器将很乐意内联虚函数:
struct B {
virtual int f() { return 42; }
};
struct D : public B {
virtual int f() { return 43; }
};
int main(int argc, char **argv) {
B b;
cout << b.f() << endl; // This call will be inlined
D d;
cout << d.f() << endl; // This call will be inlined
B& rb = rand() ? b : d;
cout << rb.f() << endl; // Must use virtual dispatch (i.e. NOT inlined)
return 0;
}
[更新:在编译时无法识别某些rb
的真实动态对象类型 - 感谢MSalters]
如果可以在编译时确定对象的类型,但该函数不可内联(例如,它很大或在类定义之外定义),则它将被非虚拟地调用。
答案 4 :(得分:1)
如果您没有C ++的语法糖,那么考虑如何用好的旧“C”编写代码有时是有益的。有时答案不是使用间接呼叫。有关示例,请参阅this answer。
答案 5 :(得分:1)
通过更改调用约定,您可以从虚拟调用中获得更好的性能。旧的Borland编译器有一个__fastcall约定,它在cpu寄存器而不是堆栈中传递参数。
如果您仍然坚持使用虚拟调用并且这些操作真的很重要,那么请检查编译器文档以获取支持的调用约定。
答案 6 :(得分:0)