2011-12-24 124 views
2

我有问题。我试图创建代码来更新我的游戏,但我陷入了一个“困境”。我不想使用虚拟的,唯一的原因是每个我谈论的人(在论坛,聊天,朋友)都说虚拟代码真的很慢,所以我做了一个研究,发现它的搜索vtable可以将性能降低近一半。所以,我将它用于不需要每帧更新的任务。一切工作正常,直到我得到更新/渲染功能。然后我开始考虑寻找解决方法。有一个想法,但首先我想问问那些在实施它之前知道的人。虚拟或类型铸造

我的游戏引擎非常受事件驱动。我可以使用子系统间的事件(图形,用户界面,脚本)发送任何类型的数据。所以,我正在考虑每帧发送一个事件“renderScene”。这听起来很棒,但有一个问题。事件处理程序的结构并不是那么好,我现在真的不想改进它,因为它做了一个非常体面的工作,我的目标是完成我的游戏而不是固定引擎并且永远不会完成它(发生我,所以不想再回到它)。

我的事件处理程序有一个函数,它将事件注册到函数(我称之为处理函数)。但该函数的问题是,我需要做功能绑定和东西来注册成员函数。所以,我找到了一个解决方法 - 我创建一个静态函数并从中调用成员函数。这是一个静态的功能到底如何模样:

void GraphicsSubsystem::renderScene(Subsystem * subsystem, Event * event) { 
    GraphicsSubsystem * graphics = static_cast<GraphicsSubsystem *>(subsystem); 
    graphics->renderScene(); 
} 

void ScriptingSubsystem::runLine(Subsystem * subsystem, Event * event) { 
    ScriptingSubsystem * scripting = static_cast<ScriptingSubsystem *>(subsystem); 
    Event1<String> * e = static_cast<Event1<String> *>(event); 
    scripting->runLine(e->getArg()); 
} 

有争论总是抽象的子系统类和基本事件类。 runLine函数我没有铸造问题,因为我没有在每一帧上运行一行代码。不过,renderScene函数让我有点不舒服。

tl; dr所以,这是我的问题。在每一帧上静态地投射一个对象比在每一帧上调用一个虚拟功能还要快吗?

+6

不要害怕使用虚拟方法。这只是过早的优化。 – Matt 2011-12-24 07:34:53

+0

我将尝试基于渲染场景的事件,因为它更符合我的设计。你是对的,我会担心建立结构,然后担心表现。游戏不会是一个巨大的MMO或任何东西,所以我应该担心这些有点事情。谢谢大家!! – Gasim 2011-12-24 07:50:12

回答

3

一般的答案取决于具体情况。

但是,试着想象运行时在调用虚拟函数时做了什么 - 它需要这个指针,在虚拟方法表中查找函数的指针并运行该函数。

对我来说,目前还不清楚为什么这应该比你的静态铸造更慢。

如果我是你,我会用这两种方法创建一个原型代码/存根,并调用它一百万次并测量性能。

6

是的,静态转换是一个相当快的操作。静态转换是静态的,即所有参数在编译时已知,并且在运行时指针被常量修改。

但是,你也不应该对虚拟函数调用过分悲观。虽然它们比正常的函数调用慢,但它们在许多时间尺度上仍然非常快,特别是与渲染场景的成本相比。我几乎无法想象一个虚拟函数每帧调用缓慢的游戏。首先做一个干净的设计,并且当你观察一个缓慢的游戏并且能够描述它确切的速度时,会担心缓慢。

+0

+1 Renderscene将比虚拟函数调用开销慢很多倍。 – Matt 2011-12-24 07:39:36

+0

问题是使用虚拟的面向对象设计几乎不可能在事实之后加速,因为缓慢在所有事情中均匀分布。为了实现真正的快速,设计必须从一开始就与最高速度的硬件相匹配。静态函数以最小的重定向处理数据数组。完全不同的设计模式。 – 2015-04-23 17:30:30

3

question讨论了静态铸造的成本,这question虚拟调度的成本。后者解释说,尽管vtable查找并不是非常昂贵,但丢失优化和缓存未命中的性能影响可能是可观的。

因此,您的担忧至少值得思考。 (我最初的想法是,在渲染场景中完成的工作肯定比调用该函数的成本要重要得多)。

@ Jiri的评论肯定是对的,但唯一真正知道的方法是来衡量它 - 我们在这里讨论的效果是非常特定于编译器/优化器/ CPU的巧妙。建立测试不需要很长时间。

1

如果您需要虚拟功能的功能,请使用它们。他们在那里,因为他们是有用的。

如果在你的系统上有一个简单而有效的方法来实现它,显然编译器实现者已经这样做了。

相信你的编译器!