加速gcc中的虚函数调用

时间:2009-04-01 00:11:09

标签: c++ virtual-functions gprof

使用gprof分析我的C ++代码,我发现我的大部分时间花在一遍又一遍地调用一个虚拟方法上。该方法本身很短,如果它不是虚拟的,可能会内联。

有什么方法可以加快这一点而不是将其重写为不是虚拟的?

7 个答案:

答案 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)