紧密循环中虚函数的成本

时间:2011-07-06 15:37:49

标签: c++ optimization virtual-functions

我遇到的情况是我的游戏对象有虚函数Update()。有很多游戏对象(目前有7000多个),并且循环调用所有这些对象(以及其他内容)的更新。我的同事建议我们应该完全删除虚函数。可以想象,这将需要大量的重构。

我见过this answer但在我的情况下,分析意味着我必须更改很多代码。所以在我开始考虑开始之前,我想我会在这里询问有关在这种情况下重构是否值得的意见。

请注意,我已经描述了循环的其他部分,并且一直在尝试优化耗时最长的部分。我怀疑在这种情况下虚拟函数调用是我不应该担心的,但我不能确定,直到我配置文件,我不能分析,直到我更改代码(这是很多)。另请注意,某些更新功能非常小,而其他功能则更复杂。

编辑:有多个答案可以提供很好的洞察力,因此将来遇到这个问题的任何人都会看看所有答案,而不仅仅是所选答案。

4 个答案:

答案 0 :(得分:10)

虚拟函数调用不会增加多于一个间接和一个难以预测的跳转。这意味着通常你是一个管道冲洗或每个虚拟功能大约20个周期。其中7000个是大约140000个周期,与平均更新功能相比,这个周期应该可以忽略不计。如果不是,请说大多数更新函数都是空的,您可以考虑将可更新对象放在单独的列表中以实现此目的。

删除虚拟功能只会导致其中一个人用相同但自行实现的系统替换它。这是虚拟功能有意义的确切位置。

每个参考,140000个周期约为50微秒。这假设一个P4有一个巨大的管道并且总是一个完整的管道冲洗(你通常不会得到)。

答案 1 :(得分:8)

虽然它不是相同的代码,可能与您使用的编译器不同,但这里有一些来自相当旧的基准测试的参考数据(Joe Orost的Bench ++):

Test Name:   F000005                         Class Name:  Style
CPU Time:        7.70  nanoseconds           plus or minus      0.385
Wall/CPU:        1.00  ratio.                Iteration Count:  1677721600
Test Description:
 Time to test a global using a 10-way if/else if statement
 compare this test with F000006


Test Name:   F000006                         Class Name:  Style
CPU Time:        2.00  nanoseconds           plus or minus     0.0999
Wall/CPU:        1.00  ratio.                Iteration Count:  1677721600
Test Description:
 Time to test a global using a 10-way switch statement
 compare this test with F000005


Test Name:   F000007                         Class Name:  Style
CPU Time:        3.41  nanoseconds           plus or minus      0.171
Wall/CPU:        1.00  ratio.                Iteration Count:  1677721600
Test Description:
 Time to test a global using a 10-way sparse switch statement
 compare this test with F000005 and F000006


Test Name:   F000008                         Class Name:  Style
CPU Time:        2.20  nanoseconds           plus or minus      0.110
Wall/CPU:        1.00  ratio.                Iteration Count:  1677721600
Test Description:
 Time to test a global using a 10-way virtual function class
 compare this test with F000006

这个特殊的结果是来自64位版本的VC ++ 9.0(VS 2008)的编译,但它与我在其他最近的编译器中看到的相似。最重要的是,虚拟功能比大多数明显的替代方案更快,并且非常接近与击败它的唯一速度相同的速度(实际上,两者相等是在测量范围内)误差范围)。但是,这取决于所涉及的密集值 - 正如您在F00007中所看到的,如果值是稀疏的,则switch语句产生的代码比虚函数调用慢。

底线:虚拟函数调用可能是错误的查找位置。重构代码可能很容易变慢,即使最多也可能无法获得足够的注意或关注。

答案 2 :(得分:6)

如果您无法进行分析,请查看汇编程序代码,以了解查找的实际成本。这可能是一个简单的间接跳跃,几乎没有任何成本。

如果你需要重构,这里有一个建议:创建许多“UpdateXxx”类,它们知道如何调用新的非虚拟update()方法。收集数组中的那些,然后在它们上面调用update()

但我的猜测是你不会节省太多,特别是只有7K的物品。

关于性能分析的注意事项:如果您不能使用分析器(让我想知道为什么不这样做),请将呼叫计时到update()并记录时间超过100毫秒的呼叫。时机并不昂贵,它可以让你快速找出最贵的电话。

答案 3 :(得分:2)

您可以在此处找到虚拟,内联和直接通话的另一项测试 [在此输入链接描述] [1] Virtual functions and performance - C++