【问题标题】:Rapidly instantiate objects - good or bad?快速实例化对象——好还是坏?
【发布时间】:2014-03-18 01:09:38
【问题描述】:

注意: 虽然这个问题与游戏没有直接关系,但我已经围绕游戏开发塑造了背景,以便更好地可视化与这个问题相关的场景。 em>

tl;dr: 快速创建同一类的对象是内存密集型、低效还是常见做法?

假设我们有一个“子弹”类 - 每次玩家“射击”时都会创建一个该类的实例 - 每秒 1 到 10 次。这些实例可能会在碰撞时(显然)被破坏。

这是一个糟糕的主意吗?一般的 OOP 在这里可以吗(即:class Bullet { short x; short y; } 等)还是有更好的方法来做到这一点? newdelete 是首选吗?

非常感谢任何输入。谢谢!

【问题讨论】:

  • 有很多方法。在栈上分配比在堆上更快,如果你需要堆,你可以使用std::vector::reserve
  • 这完全取决于您如何实例化它们...
  • 最好的方法是什么,@delnan?
  • 这取决于你如何使用它们,你需要什么以及你可以做出什么样的权衡...... ;-) 真的,没有一个最好的方法。我担心这个问题太宽泛了。

标签: c++ oop


【解决方案1】:

这听起来像是memory-poolsFree-Lists 等技术的一个很好的用例。两者的想法是你有一定数量的预先分配的元素的内存。您可以覆盖类的 new 运算符以使用池/列表或使用 placement new 在检索到的地址中实例化您的类。

优点:

  • 没有内存碎片
  • 很快

缺点:

  • 您必须事先知道最大元素数

【讨论】:

  • 这会比使用堆栈更有效还是更有效?
  • “使用堆栈”是什么意思?
  • @Adosi 很难回答。自动存储持续时间(您可能指的是“堆栈”)允许更多优化,例如将对象拆分为寄存器(如果它足够小并且您不获取它的地址并且满足其他一些条件)。另一方面,正确设计的池/空闲列表中的分配实际上与分配堆栈内存一样便宜,而且内存也可能在缓存中。
【解决方案2】:

不要只是不断地创建和删除对象。相反,另一种方法是拥有一个可以重用的常量、可调整大小的数组或对象实例列表。例如,创建一个包含 100 个子弹的数组,它们不必全部绘制,有一个 boolean 来说明它们是否“活跃”。

然后,每当您需要新子弹时,“激活”一个不活动的子弹并将其位置设置在您需要的位置。然后每当它离开屏幕时,您可以再次将其标记为非活动状态,而不必将其删除。

如果您需要更多超过 100 个子弹,只需扩展数组即可。

考虑阅读这篇文章以了解更多信息:Object Pool。它还有其他几个与游戏模式相关的主题。

【讨论】:

  • 在大多数实现中,当您“激活”一个对象时,您实际上是在实例化一个对象。你不分配内存,如果这就是你在说的。
  • @delnan 不,我的意思是,如果您愿意,可以延迟初始化前 100 个,或者如果您希望预先支付成本,可以主动初始化它们。但是,无论何时完成它们,都不要删除它们,只需将它们的值清空(或将它们标记为对渲染器不活动),并且在用完空子弹后需要一个新对象时,重置任何参数最旧的非活动子弹是(或任何您的重新激活方案)。这也有助于解决碎片化问题。
  • @zero298 我完全明白你的意思。但是“重置参数”实际上是一个构造函数调用(事实上,许多池实现就是这样做的,它们在删除对象时调用析构函数,并在要求新对象时调用放置new)。您刚刚为自己节省了一笔分配。
  • @delnan 啊,我不知道。谢谢。你有什么文章可以让我了解更多吗?
  • @zero298 我没有专门针对池的良好来源,但 DICE 的 ScopeStack 与此相关,而且非常聪明。
【解决方案3】:

分配对象时最起码的事情是函数调用(它的构造函数)。如果该分配是动态的,那么内存管理的成本也会因碎片化而在某些时候变得非常高。

每秒调用某个函数 10 次真的很糟糕吗?不。每秒 10 次动态创建和销毁许多小对象会很糟糕吗?可能。你应该这样做吗?绝对不是。

即使没有“感觉到”性能损失,也不能在立即获得最佳解决方案的情况下获得次优解决方案。

因此,您可以简单地拥有一个 std::vector 的项目符号,而不是例如 std::list 的对象,其中添加项目符号意味着附加到向量(在它达到足够大之后大小,不再需要任何内存分配),而删除意味着将要删除的元素与最后一个元素交换并将其从向量中弹出(实际上只是减少了向量大小变量)。

【讨论】:

  • 这听起来很实用。谢谢!
  • 请注意,有时可以内联构造函数。还有更好的方法可以为已删除的项目符号重新使用空间(例如,交换和弹出可以避免创建时的线性搜索)。
  • @delnan,是的。我会切换到那个。
【解决方案4】:

认为每次实例化时,堆中都有一个位置需要分配内存并创建一个新实例。这可能会影响性能。尝试使用集合并创建该集合的实例,其中包含您想要的项目符号数。

【讨论】:

  • 仅当您将“实例化”表示“调用new Bullet”时,这远非创建对象的唯一方法。
猜你喜欢
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-15
  • 1970-01-01
  • 2019-02-25
  • 2015-02-26
  • 1970-01-01
相关资源
最近更新 更多