用 gprof 分析我的 C++ 代码,我发现我的大部分时间都花在了一遍又一遍地调用一个虚拟方法上。该方法本身很短,如果它不是虚拟的,可能会被内联。
有什么方法可以加快速度,而不是将其全部重写为非虚拟的?
用 gprof 分析我的 C++ 代码,我发现我的大部分时间都花在了一遍又一遍地调用一个虚拟方法上。该方法本身很短,如果它不是虚拟的,可能会被内联。
有什么方法可以加快速度,而不是将其全部重写为非虚拟的?
您确定时间都与通话有关吗?可能是成本所在的功能本身吗?如果是这种情况,简单地内联可能会使函数从您的分析器中消失,但您不会看到太多的加速。
假设它确实是进行如此多虚拟调用的开销,那么在不使事物非虚拟化的情况下,您可以做的事情是有限的。
如果电话有时间/标志之类的提前退出,那么我通常会使用两级方法。检查是通过非虚拟调用内联的,仅在必要时才调用特定于类的行为。
例如
class Foo
{
public:
inline void update( void )
{
if (can_early_out)
return;
updateImpl();
}
protected:
virtual void updateImpl( void ) = 0;
};
时间是花在实际的函数调用上,还是花在函数本身上?
虚函数调用明显比非虚函数调用慢,因为虚函数调用需要额外的解引用。(如果您想阅读所有详细信息,请使用 Google 搜索“vtable”。) )更新:事实证明,维基百科的文章在这方面还不错。
但是,“值得注意”在这里意味着几条指令,如果它消耗了总计算的很大一部分,包括在被调用函数中花费的时间,那么这听起来像是一个考虑非虚拟化和内联的好地方。
但在接近 20 年的 C++ 中,我认为我从未见过真正发生过这种情况。我很想看看代码。
如果虚拟调用确实是瓶颈,请尝试CRTP 。
请注意,“虚拟”和“内联”并不是对立的——方法可以两者兼而有之。如果编译器可以在编译时确定对象的类型,它会很高兴地内联一个虚函数:
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]
如果对象的类型可以在编译时确定但函数不是可内联的(例如它很大或在类定义之外定义),它将被非虚拟调用。
如果您没有可用的 C++ 语法糖,考虑如何用老式的“C”编写代码有时会很有启发性。有时答案不是使用间接调用。有关示例,请参见此答案。
通过更改调用约定,您可能可以从虚拟调用中获得更好的性能。旧的 Borland 编译器有一个 __fastcall 约定,它在 cpu 寄存器中而不是在堆栈中传递参数。
如果您坚持使用虚拟调用并且这几个操作确实很重要,那么请检查您的编译器文档以获取支持的调用约定。