Sam*_*rsa 5 c++ optimization virtual-functions
我处于这样一种情况,即我拥有具有虚函数Update()的游戏对象.有很多游戏对象(目前有7000多个),并且循环调用所有这些对象(以及其他内容)的更新.我的同事建议我们应该完全删除虚函数.可以想象,这将需要大量的重构.
我已经看到了这个答案,但就我而言,分析意味着我必须改变很多代码.所以在我开始考虑开始之前,我想我会在这里询问有关在这种情况下重构是否值得的意见.
请注意,我已经描述了循环的其他部分,并一直在尝试优化耗时最长的部分.我怀疑在这种情况下虚拟函数调用是我不应该担心的,但我不能确定,直到我配置文件,我不能分析,直到我更改代码(这是很多).另请注意,某些更新功能非常小,而其他更大功能则更复杂.
编辑:有很多答案可以提供很好的见解,所以任何在将来偶然发现这个问题的人都会看看所有答案,而不仅仅是所选答案.
das*_*ndy 10
虚函数调用不会增加多于一个间接和一个难以预测的跳转.这意味着通常你是一个管道冲洗或每个虚拟功能大约20个周期.其中7000个是大约140000个周期,与平均更新功能相比,这个周期应该可以忽略不计.如果不是,请说大多数更新函数都是空的,您可以考虑将可更新对象放在单独的列表中以实现此目的.
删除虚拟功能只会导致其中一个人用相同但自行实现的系统替换它.这是虚拟功能有意义的确切位置.
每个参考,140000个周期约为50微秒.这假设P4具有巨大的管道并且始终是完整的管道冲洗(通常不会得到).
虽然它不是相同的代码,可能与您使用的编译器不同,但这里有一些来自相当旧的基准测试的参考数据(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
Run Code Online (Sandbox Code Playgroud)
这个特殊的结果是使用64位版本的VC++ 9.0(VS 2008)进行编译,但它与我在其他最近的编译器中看到的相似.最重要的是,虚拟功能比大多数明显的替代方案更快,并且非常接近与击败它的唯一速度相同的速度(实际上,两者相等在测量的误差范围内).然而,这取决于所涉及的密集值 - 正如您在F00007中所看到的,如果值是稀疏的,则switch语句产生的代码比虚函数调用慢.
底线:虚拟函数调用可能是错误的查找位置.重构代码可能很容易变慢,即使最多也可能无法获得足够的注意或关注.
如果您无法进行概要分析,请查看汇编程序代码,以了解查找的实际成本.这可能是一个简单的间接跳跃,几乎没有任何成本.
如果你需要重构,这里有一个建议:创建许多"UpdateXxx"类,它们知道如何调用新的非虚update()方法.收集数组中的那些然后调用update()它们.
但我的猜测是你不会节省太多,特别是只有7K的物体.
关于性能分析的注意事项:如果您不能使用分析器(让我想知道为什么不这样做),请将呼叫update()和记录呼叫计时超过100毫秒.时机并不昂贵,它可以让您快速找出最昂贵的电话.