我应该将哪个STL容器用于FIFO?

Gab*_*yer 84 c++ stl fifo

哪个STL容器最适合我的需求?我基本上有一个10个元素的宽容器,在这个容器中我不断使用push_back新元素pop_front(大约一百万次).

我目前正在使用a std::deque来执行任务,但是想知道是否std::list会更高效,因为我不需要重新分配自己(或者我可能会误认为std::dequestd::vector?).或者是否有更高效的容器满足我的需求?

PS我不需要随机访问

GMa*_*ckG 181

由于有无数的答案,你可能会感到困惑,但总结一下:

用一个std::queue.原因很简单:它是一个FIFO结构.你想要FIFO,你用一个std::queue.

它使你的意图对任何人,甚至你自己都清楚.A std::liststd::deque不.列表可以在任何地方插入和删除,这不是FIFO结构所要做的,并且deque可以从任一端添加和删除,这也是FIFO结构无法做到的.

这就是你应该使用的原因queue.

现在,您询问了性能.首先,永远记住这个重要的经验法则:良好的代码优先,性能最后.

原因很简单:在清洁和优雅之前追求性能的人几乎总是最后完成.他们的代码变成了一大堆,因为他们已经放弃了所有好的东西,以便真正从中获取任何东西.

通过首先编写好的,可读的代码,大多数性能问题都将自行解决.如果以后你发现你的性能不足,现在可以很容易地为你漂亮,干净的代码添加一个分析器,并找出问题所在.

所有人都说,std::queue只是一个适配器.它提供了安全的界面,但内部使用了不同的容器.您可以选择此底层容器,这样可以提供很大的灵活性.

那么,你应该使用哪个底层容器?我们知道,std::list并且std::deque都提供必要的功能(push_back(),pop_front(),和front()),所以我们如何决定?

首先,要了解分配(和解除分配)内存并不是一件容易的事,通常,因为它涉及到操作系统并要求它做某事.A list必须在每次添加内容时分配内存,并在内存消失时释放内存.

deque另一方面,A 以块的形式分配.它的分配频率低于a list.可以将其视为列表,但每个内存块可以容纳多个节点.(当然,我建议你真正了解它是如何工作的.)

因此,单独使用它deque应该表现得更好,因为它不经常处理内存.与处理常量大小数据的事实相混合,它可能不必在第一次通过数据后分配,而列表将不断分配和解除分配.

要理解的第二件事是缓存性能.走向RAM很慢,所以当CPU确实需要时,通过将一大块内存带回缓存,它可以最大限度地利用它.因为deque在内存块中分配,所以访问此容器中的元素可能会导致CPU带回容器的其余部分.现在任何进一步的访问deque都会很快,因为数据在缓存中.

这与列表不同,列表中一次分配一个数据.这意味着数据可以遍布内存中的所有位置,并且缓存性能会很差.

因此,考虑到这一点,deque应该是一个更好的选择.这就是使用a时它是默认容器的原因queue.总而言之,这仍然只是一个(非常)有根据的猜测:你必须使用deque一个测试来分析这个代码,而另一个测试则list需要确定.

但请记住:让代码使用干净的界面,然后担心性能.

约翰提出了包装listdeque将导致性能下降的担忧.再一次,他和我可以肯定地说,而不是自己描述它,但很可能编译器将内联调用queue.也就是说,当你说queue.push(),它真的只会说queue.container.push_back(),完全跳过函数调用.

再一次,这只是一个有根据的猜测,但queue与使用底层容器raw相比,使用不会降低性能.就像我之前说过的那样,使用它queue,因为它干净,易于使用,而且安全,如果它真的成为问题轮廓和测试.

  • +1 - 如果事实证明boost :: circular_buffer <>具有最佳性能,那么只需将其用作底层容器(它还提供所需的push_back(),pop_front(),front()和back() ). (9认同)
  • -1因为没有解决问题和臃肿无用的答案.这里的正确答案很简短,它是boost :: circular_buffer <>. (4认同)
  • 接受详细解释(这是我需要的,感谢花时间).至于良好的代码第一次性能最后,我不得不承认这是我最大的默认值之一,我总是尝试在第一次运行时完美地完成事情......我确实使用deque编写代码首先很难,但因为事情不是'表现和我想的一样(它应该几乎是实时的),我猜我应该改进一下.正如Neil所说,我真的应该使用一个分析器......尽管我很高兴现在犯了这些错误,但这并不重要.非常感谢你们所有人. (2认同)

Mar*_*som 27

退房std::queue.它包装了一个底层容器类型,默认容器是std::deque.

  • 编译器将消除每个额外的层*.按照你的逻辑,我们都应该只是在汇编中编程,因为语言只是一个阻碍它的shell.关键是要为作业使用正确的类型.而`queue`就是那种类型.好的代码优先,性能稍晚.地狱,大多数性能都源于首先使用好的代码. (3认同)
  • 关于std :: queue <>的事情是,如果deque <>不是你想要的(为了性能或任何原因),它是一个单行改变它使用std :: list作为后备存储 - 如GMan说道.如果你真的想用一个环形缓冲区,而不是一个列表,提振:: circular_buffer <>将在下降... STD权::队列<>是几乎可以肯定的是应该使用的"接口".它的后备存储可以随意更改. (3认同)
  • 抱歉,含糊其辞 - 我的观点是队列正是问题所要求的,而 C++ 设计者认为 deque 是此用例的一个很好的底层容器。 (2认同)
  • 这个问题没有任何迹象表明他发现表现缺乏.许多初学者不断询问针对任何给定问题的最有效解决方案,无论他们当前的解决方案是否具有可接受性. (2认同)
  • 你看我的回答了吗?双端队列在各方面都是更好的选择。“显而易见”是什么意思,你认为在论证中使用这样一个主观词有什么好处?我认为列表和双端队列同样是“明显”的选择,所以我说,“好吧,双端队列具有更好的分配和缓存性能”。我认为这使得 deque 成为“明显”的选择。“显而易见是最好的”本身并不合理。牛顿万有引力定律是“显而易见的”,现在看看它。 (2认同)

Emi*_*ier 10

在性能真正重要的地方,请查看Boost循环缓冲区库.


小智 7

在使用最古老的元素(大约一百万次)时,我不断地使用push_back新元素pop_front.

百万在计算中确实不是一个大数字.正如其他人所建议的,使用a std::queue作为您的第一个解决方案 万一它太慢,请使用分析器识别瓶颈(不要猜测!)并使用具有相同接口的不同容器重新实现.


edu*_*ffy 5

为什么不std::queue呢?它只有push_backpop_front.