【问题标题】:Need some advice to choose the proper container需要一些建议来选择合适的容器
【发布时间】:2011-06-02 14:24:48
【问题描述】:

我正在尝试为游戏引擎设计一个任务调度程序。任务可以是动画、触发控制器等。

我的问题是选择什么容器。这个想法是:当你插入一个新任务时,容器必须重新排序并将任务放在适当的位置。一旦执行,任务可能会更改并再次安排或删除。这主要是push和pop。

但是,如果可能的话,如果我可以随机访问一个元素会很好,但不是至关重要的。无论容器是否支持具有相同键的一个或多个元素。

我认为优先队列符合我的需求,但我看到它是基于向量实现的,我认为这个容器必须以某种方式进行优化以推送和弹出。

意见?

【问题讨论】:

  • 您通常以何种方式访问​​此队列?你会经常整理吗?如果/当您迭代它时,您会在迭代时访问每个任务吗?你会在排序时做同样的事情吗?任务 - 它们是指针还是任务的实际实例?
  • 嗯,我打算使用这个系统的方式是:你创建一个任务,然后把这个任务插入到系统中(这是一个推送)。添加任务会如此频繁。然后,对于每个帧,您检查它是否需要在该帧中执行,如果需要,请执行此任务(弹出),您需要执行任务,检查是否是周期性的,如果需要,则安排(推送)。我唯一的问题(小)是找到按需销毁的任务,但我知道如何解决。任务是实例化,因为任务包含对象的函子以执行真正的功能
  • 嗯,好吧,所以如果你想让 push'es 成为一个排序插入(我想这是因为我假设你的队列中有某种优先级......对吗?)并且您的数据相对较轻(一个仿函数),也许一些缓存友好(记住:保持数据关闭)二叉树是合适的?

标签: c++ stl


【解决方案1】:


(来源:adrinael.net

(原文来源:Liam Devine

【讨论】:

  • 嘿,我想发布那个! :
  • 这个的来源是什么? (我假设你没有做到 Tomalak)
  • @ChrisA:原始来源未知。实际上,我想我最好将其添加进去。
  • 请注意,这仅对具有大量元素的容器有效——在某些情况下非常大。对于简单类型(例如,指向任务的指针),std::vectorstd::list 好于任何少于几千个元素的元素,即使在随机位置插入时也是如此,std::vector 可能比 std::deque 好对于几十个元素,即使在前面插入(但这不太确定)。如果std::vector 被维护为一个堆(因为它在优先级队列中),那么它肯定比std::list 好,甚至可能比std::deque 更好。
  • @James:是的,该图描述了N 中计算需求的增长,而不是针对任何特定高或低N 的绝对比较。
【解决方案2】:

priority queue 似乎是您的最佳选择。

如您所见,pop 函数具有恒定的复杂度,而 push 函数在时间上是对数的。

【讨论】:

    【解决方案3】:

    std::vector 非常适合这个任务,特别是如果容器的“稳态”大小保持相当恒定(队列中有许多任务并没有太大差异)。

    如果您需要一个可更新队列(而 std::priority_queue 不是),我建议您使用 d_ary_heap_indirect(可以在 Boost.Graph “detail”文件夹中找到)。这是一个优先级队列,经常用于需要可更新优先级队列的 Dijkstra 和 A* 算法。无论如何,随机访问是必要的。此外,使用间接使得从队列中插入和删除非常有效。最后,您可以选择您的容器(作为模板参数),但它必须是随机访问的(因此,您可以尝试使用向量或双端队列)。 Pop 是恒定时间,推送和/或更新是日志时间,正确选择容器将使容器插入恒定摊销(并且 d_ary_heap_indirect 也会第二次摊销,所以我不会担心) .

    【讨论】:

      【解决方案4】:

      该向量针对一端的推送和弹出进行了优化。 :-)

      要确定优先级,您必须对任务进行排序。如果对象的数量相当少,即使这意味着在排序期间复制对象,向量也不会那么糟糕。

      其他容器,如链表,则需要为每个对象分配一个新节点。

      【讨论】:

        【解决方案5】:

        您可以使用std::priority_queue 指定所需的容器类型。 但是:您正在存储指针(我想,因为这听起来像您的身份 多态并具有身份),因此复制很便宜。你在管理它 作为一个堆(这就是std::priority_queue 所做的),所以插入完成 使用push_back 和一些交换(lg(n) max)。我什么都看不到 甚至有理由考虑 std::vector 以外的其他结构。

        std::priority_queue 确实隐藏了所有直接访问运算符(例如 operator[])。这样做是因为如果你修改一个条目,你就是 可能使堆无效(这是类的类不变量)。 但是,如果您确实想提供直接读取访问权限,则底层 容器只有protected,不是private,所以可以从中派生 并添加您想要的运算符。我非常想把它限制在const 然而,运营商。

        【讨论】:

        • 我想我不会使用多态。我将使用包含指向函数或仿函数的指针的类任务来执行实际任务。我将在其中使用多态。
        • 这里的问题是你是否可以复制和分配任务,如果可以,复制、分配或交换的成本是多少。 (如果合适,可以考虑 std::swap 的显式特化;这就是堆管理将使用的。)如果交换任务变得非常昂贵,std::priority_queue 就会变得不那么有趣。
        • 嗯,也许现在是考虑使用对象池和交换类指针的好时机。然后移动或复制必须便宜。谢谢!
        【解决方案6】:

        取决于您添加任务和取消任务(并可能执行它们)的频率以及有多少。

        如果你要处理大量的小任务,那么更喜欢优先队列,因为节点分配的成本可能不会像排序的 n log n 的渐近增长那样对你造成多大的伤害。

        如果您将有少量任务不断改变优先级,那么对向量进行排序可能是合理的,但您希望使用在列表几乎排序时效果很好的排序算法。

        调度是一门艺术,一旦构建它,您就必须对其进行概要分析。在这一点上可能信息太少,所以说。我倾向于优先队列,但如果性能不够,请记住其他选项。

        【讨论】:

        • 不会更改优先级。这一切都将要执行和重新调度:例如,如果每 10 帧执行一次任务,则执行,更改诸如 next_frame_to_exectue 之类的值并再次调度。但这就像重新排序。正如你所说,我目前不知道它是否会执行大量任务,所以我会密切关注性能。谢谢!!!!
        猜你喜欢
        • 1970-01-01
        • 2011-05-30
        • 2017-09-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多