【问题标题】:Queue Algorithms队列算法
【发布时间】:2011-01-15 13:29:57
【问题描述】:

我正在尝试为快速操作创建一个基于链表的队列,现在这就是我所拥有的:

template< typename T, 
          template< typename > class Allocator = default_allocator
>
class fast_queue
 {
 public:
  typedef T element_type;
  typedef T* element_ptr;

 private:
  typedef struct e_node
  {
   element_type val;
   e_node * next;
  }node_type;
  typedef node_type * node_ptr;
  node_ptr parent;
  node_ptr child;
  int      m_size;
  typedef Allocator< node_type > allocator_type;

 public:
  fast_queue()
   : parent( 0 )
   , child( 0 )
   , m_size( 0 )
  {
  }

  ~fast_queue() {
   this->clear();
  }

  void push(element_type& i)
  {
   node_ptr n = allocator_type::alloc();
   n->val = i;
   n->next = (node_ptr)0;
   if(child) {
    child->next = n;
   } else {
    parent = n;
   }
   child = n;
   m_size++;
  }

  element_type pop()
  {
   element_type ret = parent->val;
   node_ptr p = parent;
   parent = parent->next;
   m_size--;
   allocator_type::dealloc(p);
   return ret;
  }

  inline int size() const 
  {
   return m_size;
  }

  inline bool empty() const
  {
   return (m_size == 0);
  }

  void clear() 
  {
   while(!empty()) {
    pop();
   }
   child = 0;
  }
 };

非常简单,现在我遇到的问题是 clear() 函数。 释放队列中的所有节点似乎花费了太多时间(7 秒)。 所以问题是,什么可能是更好的算法?我试图理解 MSVC 对 std::deque 的实现,但代码太庞大了,我无法理解。

编辑: 队列应该是通用的,允许对任意类型的数据进行排队。 这是我的测试代码(Windows)

DWORD time1 = timeGetTime();
fast_queue<int> queue;

for(int i = 0; i < 100000; i++) {
    queue.push(i);
}
queue.clear();
cout << "Test time: " << (int)(timeGetTime() - time1) << " milliseconds" << endl;

【问题讨论】:

  • 请提供更多信息。您通常想做哪些操作?你的数据集有多大?你想在容器中存储什么数据?没有一种容器在所有情况下都是最好的,因此您需要选择一种适合您想要做的事情。
  • 我已经编辑了这个问题,我确实意识到在所有情况下都没有什么是最好的,我只是想找到一个比我使用的线性删除方法更好的算法。我可以根据数据类型专门化队列以进行更多微调,但我猜它应该与原始数据类型一样工作。
  • 您确定 std:queue 的速度不足以满足您的需要吗?
  • 是的,std::queue 对于我快速推送和弹出的需求来说非常慢。
  • SimpleButPerfect:你在推动什么?你想创建一个队列或堆栈,还是其他什么?你按什么顺序推送和弹出(即你是混合它,还是先做所有推送,然后弹出)?

标签: c++ algorithm performance queue


【解决方案1】:

您正在构建一个链表。 deque 实现在每个分配中存储了很多很多元素。但是,您可以为每个元素单独分配和取消分配。这就是您的队列如此缓慢的原因。

除此之外,标准的队列接口说您应该采用完整的分配器类型,而不是模板,尽管满足标准分配器要求的实际情况是它必须是模板。

【讨论】:

    【解决方案2】:

    您无法通过更改推送/弹出/清除算法来做很多事情,因为 95% 的时间都用于节点的分配和解除分配。但是你可以做一些事情:

    1) 为节点使用某种内存池。您可以使用池分配器(如果您不想实现自己的,则 boost::pool_alloc 是一个不错的选择),或者您可以在队列类中使用内部节点缓存。因此,您无需删除节点,只需将其推送到节点缓存中,并在创建节点时将它们从缓存中弹出。

    2) 在一个节点中存储多个项目。例如,如果您在一个节点中有 8 个项目,则您只需每 8 次 push/pop 分配/dealloc 一次。当然,这需要稍微复杂一点的代码;除了拥有指向头节点和尾节点的指针之外,您还需要它们的索引变量来跟踪实际使用的项目数量。

    【讨论】:

      【解决方案3】:

      我不得不取出所有 Allocator 的东西来编译它(在 g++ 4.40 下),但它很快就运行了。即使我推送了 100 倍以上的元素,填充队列也只需要大约半秒,清除它也只需要半秒。您是否尝试过使用 new 和 delete?

      【讨论】:

      • 分配器只是未来可能使用其他分配器的策略,但是我在 default_allocator 类中使用了 new 和 delete,因此没有区别。无论如何,我会在 g++ 下尝试,我只在 VC++ 下尝试过。如果它在 g++ 下正确运行,我不知道,但我想这是 VC++ 方面的问题?
      【解决方案4】:

      正如其他人所建议的,内存分配/释放是这里最大的性能问题。

      我建议你试试boost::circular_buffer。如果默认大小设置得足够高,它只会在其生命周期内分配一次内存。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-09
        • 1970-01-01
        相关资源
        最近更新 更多