我遇到的情况是我的游戏对象有虚函数Update()。有很多游戏对象(目前有7000多个),并且循环调用所有这些对象(以及其他内容)的更新。我的同事建议我们应该完全删除虚函数。可以想象,这将需要大量的重构。
我见过this answer但在我的情况下,分析意味着我必须更改很多代码。所以在我开始考虑开始之前,我想我会在这里询问有关在这种情况下重构是否值得的意见。
请注意,我已经描述了循环的其他部分,并且一直在尝试优化耗时最长的部分。我怀疑在这种情况下虚拟函数调用是我不应该担心的,但我不能确定,直到我配置文件,我不能分析,直到我更改代码(这是很多)。另请注意,某些更新功能非常小,而其他功能则更复杂。
编辑:有多个答案可以提供很好的洞察力,因此将来遇到这个问题的任何人都会看看所有答案,而不仅仅是所选答案。
答案 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++