循环调用同一个虚函数的开销
Posted
技术标签:
【中文标题】循环调用同一个虚函数的开销【英文标题】:Overhead of calling the same virtual function in a loop 【发布时间】:2019-12-25 08:56:06 【问题描述】:我有一些结构如下的代码:
(doStuff2()
将通过Class1
指针调用。这将在循环中调用doStuff()
。有多个类如Class3
所以Class2
将有多个模板实例。)
struct Class1
virtual void doStuff2() = 0;
;
template<typename T> struct Class2 : public Class1
virtual void doStuff() /*code*/;
void doStuff2() override
// Call this in a huge loop.
doStuff();
;
struct Class3 : public Class2<SomeType>
void doStuff() override final /*code*/;
;
//...
void someFunc(Class1 *p_class1)
p_class1->doStuff2();
我的问题:有没有办法以非虚拟方式调用doStuff()
,所以没有开销?在Class2
中将其设为纯虚拟并在Class3
中设为最终版本会有帮助吗?
在这样的循环中调用同一个虚函数会不会有虚函数的所有性能问题?
【问题讨论】:
如果你真的需要runtime dispatch,你必须使用virtual,所以你需要付费。如果没有,请编写反映这一点的代码。我们经常看到要求降低虚拟通话速度的问题。您知道单个 vpointer 调用的真正开销是多少吗?您认为现实世界应用程序的其余部分需要什么?唯一真正有帮助的:测量你的最终代码!并且相信:编译器比你想象的要好得多! 我真的不确定为什么/* code */
而不是 minimal reproducible example 以及为什么没有意义的函数名称。 doStuff()
的实现是否应该让你在其中进行循环?还是您在谈论其他循环?为什么不在那里放一个循环?对于固定类型X
,每个Class2<X>
有多少Class3
s 样式类型?是否有多个Class2
模板? (Class2
是模板类,不是类;Class3
同时似乎代表类的类别,尽管命名模式相同,Class1
a 是唯一类型?)
哦,简短的版本是“您尝试过 CRTP 吗?”这些问题旨在解码 CRTP 是否可以工作,从您的描述中不清楚。
我认为你想优化一个不存在的瓶颈。在真实程序的上下文中,虚拟调度的成本低得可笑。即使您找到了一种以更快的速度删除虚拟调度的方法,性能提升也会非常有限,以至于不会节省您花在担心它上的时间。
@churill 有dispatch代码本身,然后就是缺少内联。
【参考方案1】:
cmets 对为什么这可能是过早的优化有一些很好的建议,但要直接回答您的问题:
有没有办法以非虚拟方式调用 doStuff() 所以没有开销?
如果你有一组固定的类型,那么你可以通过标记联合的方式运行时调度非虚拟方法,ala std::variant
:
using Class1 = std::variant<Class2<SomeType1>, Class2<SomeType2>>;
/*...*/
std::visit([](auto&& c) c.doStuff(); , p_class1);
粗略地优化为一些不错的 C 风格切换:
switch (p_class1.class_type)
case CLASS2_SOMETYPE1:
((Class2<SomeType1>)p_class1).doStuff();
break;
/*...*/
在这样的循环中调用同一个虚函数会不会有虚函数的所有性能问题?
现代处理器非常擅长间接分支预测,因此您可能不会看到在循环中调用虚拟方法的开销很大。
【讨论】:
以上是关于循环调用同一个虚函数的开销的主要内容,如果未能解决你的问题,请参考以下文章