紧密循环中虚函数的成本

Sam*_*rsa 5 c++ optimization virtual-functions

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

我已经看到了这个答案,但就我而言,分析意味着我必须改变很多代码.所以在我开始考虑开始之前,我想我会在这里询问有关在这种情况下重构是否值得的意见.

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

编辑:有很多答案可以提供很好的见解,所以任何在将来偶然发现这个问题的人都会看看所有答案,而不仅仅是所选答案.

das*_*ndy 10

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

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

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


Jer*_*fin 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
Run Code Online (Sandbox Code Playgroud)

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

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


Aar*_*lla 6

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

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

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

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