【问题标题】:Pool memory allocation in a priority queue优先级队列中的池内存分配
【发布时间】:2011-03-08 22:06:43
【问题描述】:

我正在编写一个基于事件的模拟器,其中每个事件都会调用一个可以生成新事件的处理函数(节点),依此类推。 时间戳与每个事件相关联,它们需要按时间增加的顺序进行处理(但事件不一定按该顺序创建)。为此,我使用了一个简单的priority_queue<Event*>,其中 Event 是一个包含指向必须调用的处理节点的指针和时间戳的类。

所以,一切正常,但我每秒分配和释放数百万个事件,这显然是限制我的模拟器速度的原因(大约 30% 的执行时间用于 Event 对象的内存分配和释放)。

我发现了这个问题: Object pool vs. dynamic allocation 似乎我可以从对象池中受益匪浅。虽然我已经看到 Boost 提供了一些方法来做到这一点,但我不确定这对于在 priority_queue 中实现池是否实用。当谈到自定义内存分配时,我真的很迷茫。

所以我的问题是:为我的priority_queue 使用对象池是否实用/有益,如果是的话,是否有一种简单的方法可以做到这一点,也许还有一些代码示例(或至少是一个起点),最好不要在第一次立即依赖 Boost?

实际上也欢迎一些参考来了解池分配的工作原理!

谢谢。

【问题讨论】:

  • 只需确保在初始化时为您的池预分配一个大块,您将需要进行一些测量以了解随时需要的最高项目数,以避免完全动态分配

标签: c++ priority-queue event-driven


【解决方案1】:

是的,这样做非常实用。请记住,内置动态分配器被构建为尽可能快地用于各种目的——也就是说,它必须以任何大小、任何类型和任何顺序分配和取消分配。如果事先知道这不是必须的,那么可以大大降低分配和解除分配的复杂度。

在这种情况下,您提前知道您总是在分配一个事件。这使对象池成为您的目的的优秀分配器。将设计用于与 STL 对象一起使用的自定义分配器添加到 std::priority_queue 是非常实用的 - 队列在内部容器上模板化,std::vector 作为默认值,您可以在std::vector。结果应该是非常实用且易于使用的——基于值的自定义内存分配器,如对象池(据我所知)相当容易插入。

【讨论】:

    【解决方案2】:

    对象池的快速而肮脏的例子

    EventPool.h

    #include <stack>
    class Event;
    
    class EventPool
    {
     public:
       explicit EventPool(const unsigned int initSize = 0);
       ~EventPool();
       Event* getEvent();
       void returnEvent(Event* e);
     private:
       std::stack<Event*> pool_;
    };
    

    EventPool.cxx

    #include <Event.h>
    #include <EventPool.h>
    
    EventPool::EventPool(const unsigned int initSize)
    {
       for(unsigned int i = 0; i < initSize; ++i)
       {
          pool_.push(new Event());
       }
    }
    
    EventPool::~EventPool()
    {
       while(!pool_.empty())
       {
          delete pool_.top();
          pool_.pop();
       }
    }
    
    Event* EventPool::getEvent()
    {
       if(pool_.empty())
       {
          return new Event();
       }
       Event* returnValue = pool_.top();
       pool_.pop();
       return returnValue;
    }
    
    void EventPool::returnEventToPool(Event* e)
    {
       pool_.push(e);
    }
    

    通过这样做,您可以让池控制自身的大小。当另一个对象抓取一个事件时,由抓取器决定是否返回该事件或将其删除。

    【讨论】:

    • 我喜欢这个例子,它非常简单,适合我正在做的事情,它让我的性能提升了 5% 到 10%。现在,我想知道是否可以通过直接向我的 priority_queue 的容器提供自定义分配器来获得更多改进,如其他答案所建议的那样? __adjust_heap 和 __push_heap 仍有大约 25% 的执行时间花费,但我想这可能是不可避免的!如果我有时间,我可能会尝试提升......
    • 很高兴为您提供帮助!我添加了一个编辑以显示将堆栈初始化为给定大小,包括初始化第一组对象。这使您的游泳池有点建设重(前期成本),但希望避免额外的建设。您可以为 initSize 选择一个最佳值,因为显然较大的起始池大小意味着更大的内存占用。
    【解决方案3】:

    我猜你说的是std::priority_queue。是的,可以提供您自己的分配方案。

    STL 优先级队列是根据另一个容器(默认情况下,我认为是std::vector)实现的,可以将其指定为模板参数。然后你可以用你的分配器参数化这个其他容器(以通常的 STL 方式)。

    它仍然是分配器的实现。如果您不想自己做,我很确定您可以在那里找到很多(例如,您提到了 Boost)。我曾经使用a segregated pool implementation from here 进行了一些小的修改。它做得很好......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-20
      • 2012-09-23
      • 1970-01-01
      • 1970-01-01
      • 2013-02-13
      • 1970-01-01
      • 2021-08-31
      相关资源
      最近更新 更多