【问题标题】:string producer/consumer in C++: std::deque<char> or std::stringstream?C++ 中的字符串生产者/消费者:std::deque<char> 还是 std::stringstream?
【发布时间】:2012-10-03 00:57:23
【问题描述】:

在我们的应用程序中,我们有一个生成字符的类,还有一个使用它们的类。当前的实现在字符产生时动态分配字符(使用new)并在使用时删除它们(使用delete)。这一切都非常缓慢,我正在寻找替换该实现以提高其性能的方法。

我需要的语义是标准类queue 的语义:在前面推,在后面弹出。默认实现使用deque IIRC。 deque 通常是使用“块”或“块”内存实现的,因此我希望对操作系统内存分配器的调用要少得多,并且可以显着加快速度,而不会增加额外的内存使用。

但是,由于排队的数据是字符(可能是宽字符),另一种方法是使用标准输入/输出流类,即字符流stringstream。 AFAIK,他们的行为也很像队列。

有更好的先验选择吗?这两个类会有相似的分配模式吗?我可以尝试衡量两者的性能,但也许这并不重要,任何一个都足够好。在这种情况下,哪个最容易/最安全?

次要问题是生产者和消费者之间的并发性。我可以将访问限制为顺序访问(在同一个线程上),但线程安全的实现可能对当前的多核硬件在性能方面有益。

在我开始编写代码之前感谢您的智慧。

【问题讨论】:

  • 我可能会选择std::deque,因为它是专门为处理这种情况而设计的。另一种可能性是固定大小的队列,当它已满时会简单地阻塞。通过适当的大小,阻塞通常是可取的(如果一侧落后太远,阻塞可以给它更多的 CPU 时间,以便它有机会赶上)。
  • std::deque 的连续内存需求可能会影响性能,因为消费者的一些大型重新分配不够快。虽然尝试它可能是最好的答案
  • 我认为std::deque 没有连续的内存要求。事实上,Josuttis 的说法恰恰相反。

标签: c++ performance stream queue standard-library


【解决方案1】:

std::stringstream 不是队列,因为读取字符不会消耗它们。您可以简单地seekg(0) 并重新阅读相同的字符。因此,你写的越多,你消耗的内存就越多。

坚持std::queuestd::deque 作为底层实现的默认选择几乎肯定是正确的。

关于并发,在任何其他线程正在读取或写入std::queue 时写入绝对不安全。如果你想要一个高效的阻塞队列实现,你必须编写或借用一个专门为此目的构建的。

【讨论】:

  • std::deque 不需要向操作系统释放内存,即使它是空的 - 这可能是 OP 的考虑因素
  • @AdrianCornish:好点。很难想象一个理智的实现不会释放或重用从链两端掉下来的块。但这当然与保证不同。在 C++11 中,我想可以明智地使用 std::deque::shrink_to_fit
  • @MarceloCanton - 酷 - 不知道存在 - 谢谢。虽然我有新标准 - 从头到尾阅读它还没有发生;-)
  • 由于std::stringstream 不“消耗”读取字符,我只能在所有内容都适合内存时使用。这不是完全不可能的,但我需要预检
【解决方案2】:

我多年前读过这篇文章(我想,没有再读过它),当时他们实际上曾经在他们破产之前开始向我索要愚蠢的钱之前把杂志寄给我:-)。

这可能会有所帮助

http://www.drdobbs.com/parallel/lock-free-queues/208801974

作为旁注,我在处理财务数据的实时系统上工作 - 我们通常使用固定长度的队列,如果消费者无法处理,则丢弃不适合队列的数据 - 旧数据比丢失的数据更糟糕。当然,您的要求可能会有所不同。

【讨论】:

  • 并且需要注意的是:如果您要使用固定长度的队列,您通常会将其实现为循环缓冲区。
  • 实际上我们使用 SystemV IPC 队列 - 但循环更好,如果您可以阻止生产者覆盖消费者当前正在读取的数据。
  • 我读过那篇文章,以及 Herb Sutter 的后续文章。 TL;DR:这很难!至少作为第一步,我将放弃线程......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-07
  • 2020-10-03
  • 1970-01-01
  • 2012-03-12
  • 2015-12-14
  • 2018-09-24
  • 1970-01-01
相关资源
最近更新 更多